HTTParty, JSON requests and the right Content-Type
This post is about an issue I discovered whilst trying to debug an issue with a JSON request to a third party service. This request contained a JSON body where one of the fields was an array of strings but when querying the service to see what was sent - showed that the array was blank. The client used for this request makes use of HTTParty.
Initially I wanted to figure out what my client was sending through to the service. I attempted to use binding.pry
and step through the client to see if something was not functioning correctly.
The body being sent through was something similar to this:
{
"Param1": "Param1",
"Array": ["the", "and", "a"]
}
Everything seemed fine to me.
My next step was to backtrack and use Postman to check that the issue was the ruby client and not the third party.
- The body was identical
- Here the array was correctly sent to the service and correctly stored
- The headers in the Postman request matched those set in the client
An example of the response I get
{
"ID": 123,
"Param1": "Param1",
"Array": []
}
Another dead-end. So I decided to try and log the raw request the client was making. One of the tools used in this project is WebMock to mock out out clients and third party calls.
From https://tech.degica.com/en/2015/02/26/recording-http-examples/ I added this callback:
WebMock.after_request do |req, response|
request = {
uri: req.uri.to_s,
method: req.method.to_s.upcase,
headers: req.headers,
body: req.body
}
puts JSON.pretty_generate(request)
end
This allowed me to record what my client was actually doing.
The raw request
{
:uri=>"", :method=>"PUT",
:headers=>{"X-Apikey"=>"", "Accept"=>"application/json"},
:body=>"Param1=Param1&Array[]=the&Array[]=and&Array[]=a"
}
Here what was most apparent is that the body seems to have been converted into a paramter string.
(A really good resource on HTTParty and parameter strings is: https://stackoverflow.com/questions/21856373/sending-array-variables-using-httparty)
I then wrote a quick and dirty HTTParty call - in the method I was debugging - to the same endpoint but with hardcoded values and a body hash, and it worked :) I then did the exact same call but instead of the hardcoded values I used the same constructors as the previous code (the same header and body methods) and the exact same problem presented itself.
In my ensuing investigation I found that
"Content-Type" => "application/json"
was missing from the header parameters in the client. Even though the service does not need this header parameter, HTTParty does need it, otherwise it will default to converting the body into a paramter string when HTTParty is called (.get
, .post
, .put
…) and given a URI and options hash.
If HTTParty is called with the options directly added after the URI it works as expected.
HTTParty.put('https://google.com/', headers: {}, body: {})
But if it is called with an options hash this other behaviour is observed.
options = {headers: {}, body: {}}
HTTParty.put('https://google.com/', options)
format :json
after include HTTParty
also may help.
The correct raw request
{
:uri=>"", :method=>"PUT",
:headers=>{"X-Apikey"=>"", "Accept"=>"application/json"},
:body=>{"Param1": "Param1", "Array": ["the", "and", "a"]}
}
In Conclusion
I came across many people who seemed to be facing the same issue as myself but with no real solutions. The documentation did not seem to help and I found no mention of this behaviour.
By chance I was trying different configuration in desperation and came across the solution.