Sinatra controller params method coming in empty on JSON post request
Had a similar problem: Posting JSON params from java to sinatra service
I found a better solution to deal with it, by adding a middleware to do the same for me. I used rack-contrib gem. Following are the changes I did in my code:
EDIT: use git to get the specific version, where it fixes the issue when content type is application/json;charset=UTF-8
Gemfile:
gem 'rack-contrib', git: '[email protected]:rack/rack-contrib', ref: 'b7237381e412852435d87100a37add67b2cfbb63'
config.ru:
use Rack::PostBodyContentTypeParser
source: https://www.jaywiggins.com/2010/03/29/Using-Rack-middleware-to-parse-JSON/
In order to answer this question, we're first going to have to look at some HTTP requests (which are no more than simple telnet
'messages'; this can easily be recreated by hand). First, what happens when you submit a normal HTML <form>
? The POST
request will look very similar to this (probably with some extra parameters, but we don't need to worry about that right now):
POST /submit-form HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 12
name=JohnDoe
Typing that character-by-character (replacing the /sample-form
with whatever the URL is for any form action, and the Host
with your IP or hostname) will be same thing your browser would send. The important thing to learn from this is the parameter syntax: formname=formvalue
. Sinatra interprets the body of the POST
request into the params
hash using this syntax! Therefore, JSON requests, being substantially incompatible with this, will not show up in the params
hash because of this.
However, what you're doing in your before
block shows the proper solution. While params
from the above would be {'name' => 'JohnDoe'}
, request.body.read
will return the original body, name=JohnDoe
.
In knowing this, one can come to understand why your 'hacky' solution works: the original body of the POST
request gets interpreted by JSON.parse
, then gets inserted into the empty params
hash. The reason it seems hacky is because params
is a needless middleman in this example. The following should do the job:
post '/locations/new' do
@json = JSON.parse(request.body.read)
# @json now contains a hash of the submitted JSON content
end
However, a solution exercising better practice would either only respond if JSON content is provided, or respond differently if a standard form is submitted. As seen in the above example HTTP POST
request, an HTML form is identified with the application/x-www-form-urlencoded
MIME type, whereas JSON is identified with application/json
. If you want the specifics on checking the POST
request's MIME type, check out this question with some great answers on how to do this with Sinatra!