This is the code that works just fine:
require 'sinatra'
post '/foo' do
"Equals to #{params[:a]}!"
end
If I send POST
request, it returns all right:
$ ruby foo.rb -p 8888
$ curl -X POST -H 'Content-Length: 5' --data 'a=444' http://localhost:8888/foo
Equals to 444!
Then, I modify the code, adding Rack::RewindableInput::Middleware
because it's necessary for another case, where rewind
was available in earlier versions of Rack:
require 'sinatra'
use Rack::RewindableInput::Middleware
post '/foo' do
"Equals to #{params[:a]}!"
end
I'm getting this:
$ ruby foo.rb -p 8888
$ curl -X POST -H 'Content-Length: 5' --data 'a=444' http://localhost:8888/foo
Equals to !
What am I doing wrong?
This appears to be an interaction between the RewindableInput
middleware and the MethodOverride
middleware, which Sinatra adds by default.
Sinatra adds any middleware you specify with use
after its own default middleware, so incoming requests see MethodOverride
first, and then RewindableInput
.
MethodOverride
will parse the input (thus consuming the IO) if the content type indicates it is form data, looking for the _method
parameter. It puts all the data into a hash in the request env
object so that it can be used later.
RewindableInput
replaces the input object with a rewindable copy, but the input is now empty.
This wouldn’t be a problem, since the form data has already been parsed into a hash, but Rack only reuses this hash if the underlying IO object is the same. Since the input is now empty, re-parsing it produces no data.
A workaround is to swap the order of these two pieces of middleware. Use disable :method_override
to prevent Sinatra adding it by itself, then add it after RewindableInput
(alternatively you could just disable MethodOverride
if you are not using it):
disable :method_override
use Rack::RewindableInput::Middleware
use Rack::MethodOverride