Is there a way to manage URL encoding with ActiveResource? Specifically I am looking for a way to pass an email address as a parameter.
Currently my query fails as the # symbol gets URL encoded to %40, causing the lookup on the remote app to fail.
For example, the following query on the ActiveResource model Person…
Person.all(:from => :remote_find_by_email, :params => {:email => "john#example.com")
Produces the following URL
http://example.com/people/remote_find_by_email.xml?email=john%40example.com
Alternately, is there something the remote app should be doing to decode the parameter before performing the lookup?
UPDATE
Thanks to eks, I added the following method and before filter to the controller on the remote app:
before_filter :cgi_unescape_params, :only => [:remote_find_by_email]
private
def cgi_unescape_params
params.each { |k, v| params[k] = CGI.unescape(v) }
end
Try using CGI::unescape on the remote end, that should take care of any % encoded value. Cheers!
Related
In my application, the session hash can contain the keys sort and ratings (in addition to _csrf_token and session_id), depending on what action a user takes. That is, it can contain both of them, either one of them, or neither, depending on what a user does.
Now, I wish to call redirect_to in my application and, at the same time, restore any session information (sort or ratings) the user may have provided.
To do this, I want to insert whatever key-value session has currently got stored (out of sort and ratings) as query parameters in my call to redirect_to. So, the path might look something like /movies?sort=...&ratings=....
I don't know how to write the logic for this. How can I do this? And how do I go about selectively inserting query parameters while calling redirect_to? Is it even possible to do this?
Any help is greatly appreciated. Thank you in advance.
First just compose a hash containing the parameters you want - for example:
opts = session.slice(:sort, :ratings)
.merge(params.slice(:sort, :ratings))
.compact_blank
This example would contain the keys :sort, :ratings with the same keys from the parameters merged on top (taking priority).
You can then pass the hash to the desired path helper:
redirect_to foos_path(**opts)
You can either just pass a trailing hash option or use the params option to explitly set the query string:
irb(main):007:0> app.root_path(**{ sort: 'backwards' })
=> "/?sort=backwards"
irb(main):008:0> app.root_path(params: { ratings: 'XX' })
=> "/?ratings=XX"
irb(main):009:0> app.root_path(params: { })
=> "/"
An empty hash will be ignored.
If your calling redirect_to with a hash instead of a string you can add query string parameters with the params: key:
redirect_to { action: :foo, params: opts }
If you're working with an arbitrary given URL/path and want to manipulate the query string parameters you can use the URI module together with the utilities provided by Rack and ActiveSupport for converting query strings to hashes and vice versa:
uri = URI.parse('/foo?bar=1&baz=2&boo=3')
parsed_query = Rack::Utils.parse_nested_query(uri.query)
uri.query = parsed_query.except("baz").merge(x: 5).to_query
puts uri.to_s # => "/foo?bar=1&boo=3&x=5"
I am doing an http get using the url http://localhost/add?add_key[0][key]=1234&add_key[0][id]=1.
I have a rails app which gives me a neat params hash {"add_key"=>{"0"=>{"key"=>"1234", "id"=>"1"}}. However when I try to post this to a different server using
new_uri = URI.parse("http://10.10.12.1/test")
res = Net::HTTP.post_form new_uri,params
The server handling the post is seeing this parameter in the request
{"add_key"=>"0key1234id1"}
Looks like post_form requires a String to String hash. So how do I convert the params hash to
{"add_key[0][key]" => "1234", add_key[0][id]" => "1"}
From the fine manual:
post_form(url, params)
Posts HTML form data to the specified URI object. The form data must be provided as a Hash mapping from String to String.
So you're right about what params needs to be.
You could grab the parsed params in your controller:
{"add_key"=>{"0"=>{"key"=>"1234", "id"=>"1"}}
and then recursively pack that back to the flattened format that post_form expects but that would be a lot of pointless busy work. An easy way to do this would be to grab the raw URL and parse it yourself with URI.parse and CGI.parse, something like this in your controller:
u = URI.parse(request.url)
p = CGI.parse(u.query)
That will leave you with {"add_key[0][key]" => "1234", "add_key[0][id]" => "1"} in p and then you can hand that p to Net::HTTP.post_form.
Rails' URL generation mechanism (most of which routes through polymorphic_url at some point) allows for the passing of a hash that gets serialized into a query string at least for GET requests. What's the best way to get that sort of functionality, but on top of any base path?
For instance, I'd like to have something like the following:
generate_url('http://www.google.com/', :q => 'hello world')
# => 'http://www.google.com/?q=hello+world'
I could certainly write my own that strictly suits my application's requirements, but if there existed some canonical library to take care of it, I'd rather use that :).
Yes, in Ruby's standard library you'll find a whole module of classes for working with URI's. There's one for HTTP. You can call #build with some arguments, much like you showed.
http://www.ruby-doc.org/stdlib/libdoc/uri/rdoc/classes/URI/HTTP.html#M009497
For the query string itself, just use Rails' Hash addition #to_query. i.e.
uri = URI::HTTP.build(:host => "www.google.com", :query => { :q => "test" }.to_query)
Late to the party, but let me highly recommend the Addressable gem. In addition to its other useful features, it supports writing and parsing uri's via RFC 6570 URI templates. To adapt the given example, try:
gsearch = Addressable::Template.new('http://google.com/{?query*}')
gsearch.expand(query: {:q => 'hello world'}).to_s
# => "http://www.google.com/?q=hello%20world"
or
gsearch = Addressable::Template.new('http://www.google.com/{?q}')
gsearch.expand(:q => 'hello world').to_s
# => "http://www.google.com/?q=hello%20world"
With vanilla Ruby, use URI.encode_www_form:
require 'uri'
query = URI.encode_www_form({ :q => "test" })
url = URI::HTTP.build(:host => "www.google.com", query: query).to_s
#=> "http://www.google.com?q=test"
I would suggest my iri gem, which makes it easy to build a URL through a fluent interface:
require 'iri'
url = Iri.new('http://google.com/')
.append('find').append('me') # -> http://google.com/find/me
.add(q: 'books about OOP', limit: 50) # -> ?q=books+about+OOP&limit=50
.del(:q) # remove this query parameter
.del('limit') # remove this one too
.over(q: 'books about tennis', limit: 10) # replace these params
.scheme('https') # replace 'http' with 'https'
.host('localhost') # replace the host name
.port('443') # replace the port
.path('/new/path') # replace the path of the URI, leaving the query untouched
.cut('/q') # replace everything after the host and port
.to_s # convert it to a string
I'm currently creating both the client and server app using ActiveResource for web servicing. The client has a long string (:history) that needs a conversion process done by the server.
Here, the client calls the post method on my object which extends ActiveResource::Base
active_resource.post(:convert, {:history => hh, :format => format})
This line errors complaining that the URI is too long:
ActiveResource::ClientError Failed. Response code = 414. Response message = Request-URI Too Large.
What other options do I have for sending "large" data ? Probably looking in the neighborhood of 2000 characters of data for the hh string above.
Thanks!
So the signature for the post method is:
post(custom_method_name, options = {}, body = '')
So, when you do:
active_resource.post(:convert, {:history => hh, :format => format})
It's putting your post variables in the options hash, which comes out in your query string for the post.
What you want to do is:
active_resource.post(:convert, nil, {:history => hh, :format => format}.to_json)
I didn't think post parameters factored into URI length. Are you sure it's the hh string and not the actual URI that active_resource.post is using?
Hi
I want to use two params hashes in one page
The job of this page is straightforward, it's an edit page, and I want it to send out notifications to a server once the editing job is done.
def update
#description = Tempdescription.find(params[:id])
#description.update_attributes(params[:tempdescription])
sendnotification
end
def sendnotification
params[:to_ids]="xxxx"
sig = hash_params(params);
params[:sig] = sig
response = RestClient.post "http://api.xxxx.com/restserver.do", params, :content_type => :json, :accept => :json
render :text=>response
end
def hash_params(params)
params = Hash[*params.sort.flatten]
payload = ''
params.sort.each do |pair|
key, value = pair
payload = payload + "#{key}=#{value}"
end
return Digest::MD5.hexdigest(payload + API_SECRET)
end
Not surprisingly the params in sendnotification also includes params used for updating
and the server returns 104 error
Therefore,
I tried
new_params=Hash[]
and use new_params to replace the old params in sendnotification
But then rails complains
undefined method `<=>' for :session_key:Symbol
app/controllers/tempdescriptions_controller.rb:72:in `<=>'
app/controllers/tempdescriptions_controller.rb:72:in `sort'
app/controllers/tempdescriptions_controller.rb:72:in `hash_params'
app/controllers/tempdescriptions_controller.rb:45:in `sendnotification'
So I am thinking if there is any way I can create another params?
Thanks in advance
Ok, having complained about your formatting I suppose I should hazard an attempt at your problem.
This code:
def hash_params(params)
params = Hash[*params.sort.flatten]
payload = ''
params.sort.each do |pair|
key, value = pair
payload = payload + "#{key}=#{value}"
end
return Digest::MD5.hexdigest(payload + API_SECRET)
end
.. appears to accept a hash as its argument and then recreate it with the keys sorted. Presumably this code is targeted at ruby 1.9 otherwise that would be rather pointless. It then sorts again for no reason I can determine before joining the keys and values with = but without separating the pairs with &.
The error is a little mysterious though; I have no trouble sorting symbols with ruby 1.9. Perhaps you're running ruby 1.8?
Ok...after playing with rails console for a while I finally find a solution to this problem.
In sendnotification method I created a new hash
p=Hash[]
but simply putting this will not work, as I mentioned before.
Then I changed all
p[:key]
to
p["key"]
and it works.
Obviously Hash#sort doesn't work with hash[:key] if the hash is newly created but it works with params and that's what puzzled me and made me believe there is a difference between params and normal hash.
I am using Ruby 1.8.7 so I think it might just be a bug of this version.