http icon indicating copy to clipboard operation
http copied to clipboard

Error with query String params

Open imadmoussa1 opened this issue 8 years ago • 11 comments

I am using the http gem in a rails api project. I have a problem while making a "get" call to another rails api using the :params option . this function convert those params to string because of this they are not parsed.

Is there any solution to send "query params" instead of "query string params" ?

the query params sent using this gem : /comments?filter=%7B%22title%22%3D%3E%22%22%2C+%22language%22%3D%3E%22%22%2C+%22region%22%3D%3E%22%22%2C+%22active%22%3D%3E%22%22%2C+%22

the query params form that i am seeking : /comments?filter[post]=1,2&filter[author]=12 (JSON api filter form )

Thank you.

imadmoussa1 avatar Aug 31 '17 10:08 imadmoussa1

Can you post a code example? I very strongly suspect this is something wrong in your usage, and not a bug in the gem.

tarcieri avatar Aug 31 '17 16:08 tarcieri

I've found this gem doesn't handle nested params natively and have resorted to using ActiveSupport's #to_query method

This request:

HTTP.get('https://requestb.in/qzba87qz', params: {filter: {post: '1,2', author: 12}})

makes this query request:

filter=%7B%3Apost%3D%3E%221%2C2%22%2C+%3Aauthor%3D%3E12%7D

which decodes to:

filter={:post=>"1,2",+:author=>12}

Using #to_query in a Rails app to construct the query string works right, though:

HTTP.get("https://requestb.in/qzba87qz?#{{filter: {post: '1,2', author: 12}}.to_query}")

makes this query request:

filter%5Bauthor%5D=12&filter%5Bpost%5D=1%2C2

decodes to:

filter[author]=12&filter[post]=1,2

mikegee avatar Sep 01 '17 01:09 mikegee

Dear @tarcieri , i am using this code in my project HTTP.get(url, params: { filter: { post: '1,2', author: 12 } }). and the solution of Mr. @mikegee worked well , thanks a lot .

imadmoussa1 avatar Sep 01 '17 07:09 imadmoussa1

We use Rack::Utils.parse_nested_query and Rack::Utils.build_nested_query to work around this.

britishtea avatar Sep 01 '17 09:09 britishtea

As far as I could tell, there isn't such thing as "nested query parameters" in the URI RFC. The discussion in https://github.com/sporkmonger/addressable/issues/77 provides some more context on that.

In short, Rails and some other web frameworks invented the filter[author] convention, as a way to specify nested parameters. That's why only Rack knows how to handle it, while URI and Addressable::URI don't (as far as I could tell).

I vote for keeping this functionality out of HTTP.rb, because we cannot pull it from URI nor Addressable::URI since it doesn't exist, we cannot depend on Rack/ActiveSupport either, and it wouldn't make sense for HTTP.rb to implement its own. Ultimately it's up to the Addressable maintainers to decide whether they will support nested parameters like this (though it seems they won't). So I think we can close this issue.

Note that there is also an alternative way which IMO is perfectly acceptable:

HTTP.get('https://requestb.in/qzba87qz', params: {"filter[post]" => "1,2", "filter[author]" => "2"})

janko avatar Feb 02 '18 03:02 janko

@janko-m generally I totally agree. :D But I think we should provide swap-able interface for things like this.

ixti avatar Feb 02 '18 12:02 ixti

So, let's keep this open for now.

ixti avatar Feb 02 '18 12:02 ixti

What about providing a way to pass query parameters in their raw form (like body)? This would satisfy the existing implementation :params and more complex usages

Fire-Dragon-DoL avatar Feb 06 '24 00:02 Fire-Dragon-DoL

@Fire-Dragon-DoL there's already a workaround of that nature posted above: https://github.com/httprb/http/issues/428#issuecomment-362472654

tarcieri avatar Feb 06 '24 00:02 tarcieri

@Fire-Dragon-DoL there's already a workaround of that nature posted above: #428 (comment)

I saw that, however it introduces an additional challenge because of the double transformation, especially in contexts where trying to "proxy" query parameters from another HTTP request:

Query (string) format -> Rails format -> HTTPrb format -> HTTP Request

With the possibility to make mistakes between "rails format" and the HTTPrb format. If it was possible to pass a raw string for the query parameters, this would be the flow instead:

Query (string) format -> HTTP Request

It's also more likely that a library supports transforming into a query string than into the HTTPrb "params" format (which effectively is not really a format, rather a solution to a problem, I understand that).

Right now my solution was instead to take the query parameters, convert them to a query string and append them to the URL itself. Something along the line this sudo-code:

uri = URI(some_base_url)
uri.query = Rack::Utils.build_nested_query({ foo: ["bar", "baz"] })
HTTP.get(uri.to_s) # params entirely omitted

Fire-Dragon-DoL avatar Feb 06 '24 01:02 Fire-Dragon-DoL

I believe there would be no harm in adding query: keyword (like we have for json: and form:). Or, we can allow passing String to params in which case we will treat it as is.

Either way, we need to think how to merge params if multiple sources are provided.

Currently, HTTP.get("https://example.com?a=1&b=2", params: { b: 3, c: 4 }) will send:

GET https://example.com/?a=1&b=2&b=3&c=4

Which is a bit confusing IMO. And even more so if you run:

HTTP.get("https://example.com?a=1&b=2", params: { b: 3, c: 4 }, json: { d: 5 }, form: { e: 6 })

Probably, the best would be to raise an ArgumentError in such case.

ixti avatar Feb 06 '24 04:02 ixti