You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向rack/test的put()方法传递原始二进制数据并避免编码错误?

问题

执行以下代码发送二进制数据(比如ZIP归档)时:

require 'rack/test'
data = # some binary data, for example, ZIP archive
put('/foo', data, 'content_type' => 'application/octet-stream')

原本运行正常,但当data中包含%符号时,会抛出错误:

Invalid query parameters: invalid %-encoding ... (long exception text)

不想对data做Base64或CGI编码,因为生产环境中服务器端代码运行正常,问题出在Rack的使用方式上,需要让它原样发送数据到Sinatra。

以下是可复现问题的代码(直接运行即可查看输出):

require 'sinatra'
require 'minitest/autorun'
require 'rack/test'

put '/foo' do
  'OK'
end

class FooTest < Minitest::Test
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end

  def test_me
    put('/foo', 'hello % world')
    assert_equal(200, last_response.status, last_response.body)
  end
end

运行后得到结果:

1) Failure:
FooTest#test_me [a.rb:18]:
Invalid query parameters: invalid %-encoding (hello % world).
Expected: 200
  Actual: 400

移除%符号后测试就能通过。

解决方案

问题核心是:当使用put(path, data, headers)这种调用方式时,Rack::Test会把第二个参数data当成URL查询参数解析,而非请求体,因此会尝试对%做URL解码,最终触发编码错误。

要让Rack::Test把数据作为请求体原样发送,有两种可行方式:

方式一:使用:input选项指定请求体

修改测试中的put调用,将查询参数位置传空哈希,通过:input选项传递请求体内容:

put('/foo', {}, {'content_type' => 'application/octet-stream'}, input: 'hello % world')

方式二:通过块传递请求体

利用Rack::Test的块语法,将块的返回值作为请求体:

put '/foo', nil, 'content_type' => 'application/octet-stream' do
  'hello % world'
end

两种方式都能让Rack跳过对请求体的URL解析,直接将数据原样发送给Sinatra。修改后的完整测试代码示例:

require 'sinatra'
require 'minitest/autorun'
require 'rack/test'

put '/foo' do
  'OK'
end

class FooTest < Minitest::Test
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end

  def test_me
    # 任选一种方式即可
    put('/foo', {}, {'content_type' => 'application/octet-stream'}, input: 'hello % world')
    # put '/foo', nil, 'content_type' => 'application/octet-stream' do
    #   'hello % world'
    # end
    assert_equal(200, last_response.status, last_response.body)
  end
end

运行修改后的代码,测试即可正常通过,数据会原样发送到服务端。

内容的提问来源于stack exchange,提问作者yegor256

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:02:13