<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>grd0n9 (有鲤)</title>
    <link>https://ruby-china.org/grd0n9</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>post 请求时遇到一个很诡异的问题，求解答</title>
      <description>&lt;p&gt;与第三方接口对接，他发来一个 upload 的 POST 请求，然后报 500 错误，我看日志，没走到 route 就报错了&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Unexpected error &lt;span class="k"&gt;while &lt;/span&gt;processing request: undefined method &lt;span class="sb"&gt;`&lt;/span&gt;force_encoding&lt;span class="s1"&gt;' 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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;block &lt;span class="o"&gt;(&lt;/span&gt;2 levels&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;parse&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;block &lt;span class="k"&gt;in &lt;/span&gt;parse&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;parse&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/request.rb:375:in &lt;span class="sb"&gt;`&lt;/span&gt;parse_multipart&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:39:in &lt;span class="sb"&gt;`&lt;/span&gt;method_override_param&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:15:in &lt;span class="sb"&gt;`&lt;/span&gt;call&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;call&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;call&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/engine.rb:518:in &lt;span class="sb"&gt;`&lt;/span&gt;call&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;call&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/connection.rb:84:in &lt;span class="sb"&gt;`&lt;/span&gt;catch&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/thin-1.6.4/lib/thin/connection.rb:53:in &lt;span class="sb"&gt;`&lt;/span&gt;process&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/eventmachine-1.2.0.1/lib/eventmachine.rb:194:in &lt;span class="sb"&gt;`&lt;/span&gt;run_machine&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;start&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;run&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;start&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;tap&lt;span class="s1"&gt;'
    /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'&lt;/span&gt;
    /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 &lt;span class="sb"&gt;`&lt;/span&gt;run_command!&lt;span class="s1"&gt;'
    /Users/HD/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.4/lib/rails/commands.rb:17:in `&amp;lt;top (required)&amp;gt;'&lt;/span&gt;
    bin/rails:4:in &lt;span class="sb"&gt;`&lt;/span&gt;require&lt;span class="s1"&gt;'
    bin/rails:4:in `&amp;lt;main&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;报错主要在/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 里，&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;  &lt;span class="c1"&gt;#这里没有成功匹配&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;RFC2183&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DISPPARM&lt;/span&gt;&lt;span class="p"&gt;)][&lt;/span&gt;&lt;span class="s1"&gt;'filename'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^"(.*)"$/&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="no"&gt;BROKEN_QUOTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BROKEN_UNQUOTED&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$1&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/%.?.?/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/%[0-9a-fA-F]{2}/&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unescape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;scrub_filename&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;!~&lt;/span&gt; &lt;span class="sr"&gt;/\\[^\\"]/&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\\(.)/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'\1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;filename&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;case head 的时候没有匹配成功 RFC2183 或是 BROKEN_QUOTED/BROKEN_UNQUOTED
他的 headers 是&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"Content-Disposition: form-data;name=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;;filename=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;96.jpg&lt;/span&gt;&lt;span class="se"&gt;\"\r\n&lt;/span&gt;&lt;span class="s2"&gt;Content-Type:application/octet-stream&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我自己测试的 headers 是&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"Content-Disposition: form-data; name=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; filename=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;96.jpg&lt;/span&gt;&lt;span class="se"&gt;\"\r\n&lt;/span&gt;&lt;span class="s2"&gt;Content-Type: image/png&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;区别在 filename 前我有一个空格，对方没有空格
rack 的正则 filename 前就直接是一个\s了，只修改 rack 这一个正则的话，就不报 500 错误了，但又导致一些其他的问题，比如获取不到 content-type 的值&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="sr"&gt;/^(?i-mx:Content-Disposition:\s*(?-mix:[^\s()&amp;lt;&amp;gt;,;:\\"\/\[\]?=]+)\s*).*;\sfilename="(.*?)"(?:\s*$|\s*;\s*(?-mix:[^\s()&amp;lt;&amp;gt;,;:\\"\/\[\]?=]+)=)/i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我用 rails、php 原生方法或者 postman 测试，都会自动带上一个空格；
对方用的是 java，沟通后对方觉得不是他的问题，我在谷歌也没有搜到关于这个空格的案例，然后在 stackoverflow 看了看 java 相关的例子，发现有些高票答案是这么写的&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;writeBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Disposition: form-data; name=\""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attachmentName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\";filename=\""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; 
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attachmentFileName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;crlf&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也是没有带上空格的，难道真的是这个空格导致的问题吗？&lt;/p&gt;</description>
      <author>grd0n9</author>
      <pubDate>Mon, 08 Aug 2016 11:02:51 +0800</pubDate>
      <link>https://ruby-china.org/topics/30763</link>
      <guid>https://ruby-china.org/topics/30763</guid>
    </item>
  </channel>
</rss>
