Это код, который работает отлично:
require 'sinatra'
post '/foo' do
"Equals to #{params[:a]}!"
end
Если я отправлю запрос POST, он вернет все в порядке:
$ ruby foo.rb -p 8888
$ curl -X POST -H 'Content-Length: 5' --data 'a=444' http://localhost:8888/foo
Equals to 444!
Затем я модифицирую код, добавляя Rack::RewindableInput::Middleware, потому что это необходимо для другого случая, где rewind был доступен в более ранних версиях Rack:
require 'sinatra'
use Rack::RewindableInput::Middleware
post '/foo' do
"Equals to #{params[:a]}!"
end
Я получаю это:
$ ruby foo.rb -p 8888
$ curl -X POST -H 'Content-Length: 5' --data 'a=444' http://localhost:8888/foo
Equals to !
Что я делаю не так?

Похоже, это взаимодействие между промежуточным программным обеспечением RewindableInput и промежуточным программным обеспечением MethodOverride, которое Синатра добавляет по умолчанию.
Sinatra добавляет любое промежуточное программное обеспечение, которое вы указываете с помощью use, после своего собственного промежуточного программного обеспечения по умолчанию, поэтому входящие запросы сначала видят MethodOverride, а затем RewindableInput.
MethodOverride будет анализировать входные данные (таким образом, потребляя IO), если тип контента указывает, что это данные формы, ища параметр _method. Он помещает все данные в хэш объекта запроса env, чтобы их можно было использовать позже.
RewindableInput заменяет объект ввода копией с возможностью перемотки, но ввод теперь пуст.
Это не будет проблемой, поскольку данные формы уже были преобразованы в хеш, но Rack повторно использует этот хеш только в том случае, если базовый объект ввода-вывода тот же. Поскольку ввод теперь пуст, его повторный анализ не дает данных.
Обходной путь — поменять порядок этих двух частей промежуточного программного обеспечения. Используйте disable :method_override, чтобы Синатра не добавлял его сам по себе, а затем добавьте его после RewindableInput (в качестве альтернативы вы можете просто отключить MethodOverride, если вы его не используете):
disable :method_override
use Rack::RewindableInput::Middleware
use Rack::MethodOverride
Это работает. Кстати, использование
RewindableInputв нашем приложении требуется только при запуске сервера сrackup. Начиная с puma, приложение использует объектPuma::NullIOв качестве тела ответа, который по-прежнему отвечает наrewind.