webmock
webmock copied to clipboard
Query string with multiple values for the same field parsed incorrectly
I ran into an issue that might or might be related to #365, at least it seems to be caused by WebMock's way of parsing query parameters.
Consider the following URL:
http://example.org/?key=value1&key=value2
Stubbing requests with URLs like this leads to WebMock discarding all values except one. Which means in this case it will gladly accept requests to
http://example.org/?key=value2
which it really shouldn't.
Example code (based on the one in #365):
require "net/http"
require "cgi"
require "webmock"
WebMock.stub_request(:any, /.*/).
with { |req| puts "AFTER : #{URI::decode(req.uri.query)}"; true }
uri = URI.parse("http://example.org/?key=value1&key=value2")
puts "BEFORE: #{uri.query}"
req = Net::HTTP::Get.new(uri.to_s)
Net::HTTP.new(uri.host, uri.port).request(req)
produces
BEFORE: key=value1&key=value2
AFTER : key=value2
Default subscript
notation when serializing query params doesn't support multiple keys with the same name.
Please try flat_array
WebMock::Config.instance.query_values_notation = :flat_array
Thanks, that works great for query parameters. However it seems that this options breaks how the expected body is being handled.
Example:
require "net/http"
require "cgi"
require "webmock"
WebMock::Config.instance.query_values_notation = :flat_array
WebMock.stub_request(:any, /.*/).
with(:body => {'bodykey' => 'bodyval'}) { |req| puts "AFTER : #{URI::decode(req.uri.query)}"; true }
uri = URI.parse("http://example.org/?key=value1&key=value2")
puts "BEFORE: #{uri.query}"
req = Net::HTTP::Post.new(uri.to_s)
req.set_form_data('bodykey' => 'bodyval')
Net::HTTP.new(uri.host, uri.port).request(req)
produces
Real HTTP connections are disabled. Unregistered request: POST http://example.org/?key=value1&key=value2 with body 'bodykey=bodyval' with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Ruby'} (WebMock::NetConnectNotAllowedError)
You can stub this request with the following snippet:
stub_request(:post, "http://example.org/?key=value1&key=value2").
with(:body => [["bodykey", "bodyval"]],
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
registered request stubs:
stub_request(:any, "/.*/").
with(:body => {"bodykey"=>"bodyval"})
This example works fine if I set query_values_notation
back to subscript
.
It also works when I replace the hash in the expected body {'bodykey' => 'bodyval'}
with a string 'bodykey=bodyval'
IMO, for array parameters it should be passed as key[]=val1&key[]=val
I'm having the same issue. I'm stubbing an API that expects query parameters as key=value1&key=value2
.
In Faraday HTTP library this is implemented by FlatParamsEncoder. But there is no equivalent in WebMock.