与第三方接口对接,他发来一个 upload 的 POST 请求,然后报 500 错误,我看日志,没走到 route 就报错了
Unexpected error while processing request: undefined method `force_encoding' for nil:NilClass
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart/parser.rb:195:in `tag_multipart_encoding'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart/parser.rb:75:in `block (2 levels) in parse'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart/parser.rb:250:in `get_data'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart/parser.rb:74:in `block in parse'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart/parser.rb:56:in `loop'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart/parser.rb:56:in `parse'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart.rb:25:in `parse_multipart'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/request.rb:375:in `parse_multipart'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/request.rb:207:in `POST'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:39:in `method_override_param'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:27:in `method_override'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:15:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/runtime.rb:18:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/activesupport-4.2.4/lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/lock.rb:17:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/actionpack-4.2.4/lib/action_dispatch/middleware/static.rb:116:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/sendfile.rb:113:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/engine.rb:518:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/application.rb:165:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/content_length.rb:15:in `call'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/connection.rb:86:in `block in pre_process'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/connection.rb:84:in `catch'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/connection.rb:84:in `pre_process'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/connection.rb:53:in `process'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/connection.rb:39:in `receive_data'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/eventmachine-1.2.0.1/lib/eventmachine.rb:194:in `run_machine'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/eventmachine-1.2.0.1/lib/eventmachine.rb:194:in `run'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/backends/base.rb:73:in `start'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/server.rb:162:in `start'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/handler/thin.rb:19:in `run'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/server.rb:286:in `start'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/commands/server.rb:80:in `start'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:80:in `block in server'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:75:in `tap'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:75:in `server'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/commands.rb:17:in `<top (required)>'
bin/rails:4:in `require'
bin/rails:4:in `<main>'
报错主要在/Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/multipart/parser.rb 的 get_filename 里,
def get_filename(head)
filename = nil
case head #这里没有成功匹配
when RFC2183
filename = Hash[head.scan(DISPPARM)]['filename']
filename = $1 if filename and filename =~ /^"(.*)"$/
when BROKEN_QUOTED, BROKEN_UNQUOTED
filename = $1
end
return unless filename
if filename.scan(/%.?.?/).all? { |s| s =~ /%[0-9a-fA-F]{2}/ }
filename = Utils.unescape(filename)
end
scrub_filename filename
if filename !~ /\\[^\\"]/
filename = filename.gsub(/\\(.)/, '\1')
end
filename
end
case head 的时候没有匹配成功 RFC2183 或是 BROKEN_QUOTED/BROKEN_UNQUOTED 他的 headers 是
"Content-Disposition: form-data;name=\"file\";filename=\"96.jpg\"\r\nContent-Type:application/octet-stream\r\n"
我自己测试的 headers 是
"Content-Disposition: form-data; name=\"file\"; filename=\"96.jpg\"\r\nContent-Type: image/png\r\n"
区别在 filename 前我有一个空格,对方没有空格 rack 的正则 filename 前就直接是一个\s了,只修改 rack 这一个正则的话,就不报 500 错误了,但又导致一些其他的问题,比如获取不到 content-type 的值
/^(?i-mx:Content-Disposition:\s*(?-mix:[^\s()<>,;:\\"\/\[\]?=]+)\s*).*;\sfilename="(.*?)"(?:\s*$|\s*;\s*(?-mix:[^\s()<>,;:\\"\/\[\]?=]+)=)/i
我用 rails、php 原生方法或者 postman 测试,都会自动带上一个空格; 对方用的是 java,沟通后对方觉得不是他的问题,我在谷歌也没有搜到关于这个空格的案例,然后在 stackoverflow 看了看 java 相关的例子,发现有些高票答案是这么写的
request.writeBytes("Content-Disposition: form-data; name=\"" +
this.attachmentName + "\";filename=\"" +
this.attachmentFileName + "\"" + this.crlf);
也是没有带上空格的,难道真的是这个空格导致的问题吗?