在Sinatra Ruby应用中使用Rack::RewindableInput::Middleware遇参数丢失问题
问题:添加Rack::RewindableInput中间件后Sinatra无法获取POST参数
正常运行的代码:
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版本场景),添加Rack::RewindableInput::Middleware后代码变为:
require 'sinatra' use Rack::RewindableInput::Middleware 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 !
请问哪里操作出错了?
问题原因及解决办法
问题出在Rack::RewindableInput::Middleware的全局使用方式上。这个中间件会提前读取整个请求体输入流并缓存,以实现回退功能,但它没有将读取后的流重置到起始位置。当Sinatra的参数解析器后续尝试读取请求体时,流已经处于末尾状态,自然读不到a=444参数,导致params[:a]为空。
解决办法:
- 避免全局挂载该中间件,仅在需要回退输入流的代码逻辑中手动包装流:
require 'sinatra' require 'rack/rewindable_input' post '/foo' do # 仅在需要回退时,用Rack::RewindableInput包装请求体 rewound_body = Rack::RewindableInput.new(request.body) # 示例:读取流后执行回退操作 content = rewound_body.read rewound_body.rewind # 若需要重新解析参数,手动调用Rack的参数解析方法 parsed_params = Rack::Utils.parse_query(rewound_body.read) "Equals to #{parsed_params['a']}!" end - 如果场景必须使用中间件形式,需确保中间件在参数解析流程之后执行,但这种方式并不稳妥——参数解析依赖请求体流的原始状态,更可靠的方式仍是按需手动包装流,而非全局挂载中间件。
内容的提问来源于stack exchange,提问作者yegor256
相关产品推荐
相关产品推荐

