12

我们的 ruby​​ on rails 网站有一个 URI,我们的合作伙伴之一将 XML 数据发布到该 URI。

由于我们不想处理 XML,我们实际上只是将原始数据填充到数据库列中,并且不再进一步处理它。

但是,我们收到的一篇帖子在空气制动中给了我们这个错误:

ArgumentError: invalid %-encoding ("http://ns.hr-xml.org/2004-08-02" 
userId="" password=""><BackgroundReportPackage type="report">
<ProviderReferenceId>....

使用回溯:

vendor/ruby-1.9.3/lib/ruby/1.9.1/uri/common.rb:898:in `decode_www_form_component'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:41:in `unescape'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:94:in `block (2 levels) in parse_nested_query'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:94:in `map'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:94:in `block in parse_nested_query'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:93:in `each'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:93:in `parse_nested_query'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/request.rb:332:in `parse_query'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/request.rb:209:in `POST'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/methodoverride.rb:26:in `method_override'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/methodoverride.rb:14:in `call'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/runtime.rb:17:in `call'
vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/cache/strategy/local_cache.rb:72:in `call'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/lock.rb:15:in `call'
vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.13/lib/action_dispatch/middleware/static.rb:63:in `call'
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:136:in `forward'
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:143:in `pass'
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:155:in `invalidate'
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:71:in `call!'
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:51:in `call'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/engine.rb:479:in `call'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/application.rb:223:in `call'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/railtie/configurable.rb:30:in `method_missing'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/deflater.rb:13:in `call'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/content_length.rb:14:in `call'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/rack/log_tailer.rb:17:in `call'
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:80:in `block in pre_process'
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:78:in `catch'
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:78:in `pre_process'
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:53:in `process'
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:38:in `receive_data'
vendor/bundle/ruby/1.9.1/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run_machine'
vendor/bundle/ruby/1.9.1/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run'
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/backends/base.rb:63:in `start'
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/server.rb:159:in `start'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/handler/thin.rb:13:in `run'
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/server.rb:268:in `start'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands/server.rb:70:in `start'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:55:in `block in <top (required)>'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:50:in `tap'
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:50:in `<top (required)>'
script/rails:6:in `require'
script/rails:6:in `<main>'

问题是 POST 包含数据:

<ChargeOrComplaint>DRIVE WHILE BLOOD ALCOHOL LEVEL IS 0.08% OR MORE</ChargeOrComplaint>

大概这是有效的 XML,但最后的裸体%导致0.08%错误,因为它是通过 HTTP 来的,我猜 rack 期望它是 URL 编码的。

回溯表明这甚至在它到达我们的代码之前就已经发生了,所以我认为这与我们如何处理它没有任何关系。

那么我的问题:

1)问题出在哪里?Ruby 1.9.3 的实现decode_www_form_component(在堆栈跟踪的顶部)?架子?我们合作伙伴的 POST 数据或标头?我们对 POST 的处理?

2) 通过 HTTP 发布的 XML 数据是否需要进行 URL 编码?

3) 这个 POST 是否需要一个标题才能让 Rack 正确解释它?(即:它是 XML 二进制数据,而不是 URL 编码的)。

4) 如果我无法让我们的合作伙伴更改他们发布给我们的内容,我们该如何解决?一些机架中间件?

4

3 回答 3

8

我猜你的搭档可能会将数据以“x-www-form-urlencoded”的形式发布给你,让 Rack 尝试以这种方式解析它。如果他们可以更改他们发送的内容,我怀疑将他们的内容类型设置为“text/xml”将解决这个问题。

如果你不能让他们改变他们发送的内容,那么是的,我认为你必须使用 Rack 中间件(或猴子补丁)。尽管您可以浏览 Rack 源代码,但也许有一个设置可以避免进行任何解析。

于 2013-04-02T17:15:43.557 回答
2

关于在请求正文内容中捕获无效编码错误的责任在各个论坛上存在一些争论,但是 rack 和 rails 都没有处理它,都把它留给了应用程序来处理。为了解决我的应用程序中 POST 数据中的无效 %-encoding 问题,我对这个相关问题使用了类似的解决方案:Rails ArgumentError: invalid %-encoding

我添加了这个中间件app/middleware/invalid_post_data_interceptor.rb来拦截无效的帖子数据:

class InvalidPostDataInterceptor
  def initialize(app)
    @app = app
  end

  def call(env)
    request_content = Rack::Request.new(env).POST rescue :bad_form_data

    headers = {'Content-Type' => 'text/plain'}

    if request_content == :bad_form_data
      [400, headers, ['Bad Request']]
    else
      @app.call(env)
    end
  end
end

然后将其添加到中间件堆栈,方法是将其添加到application.rb

config.middleware.insert_before Rack::Runtime, "InvalidPostDataInterceptor"
于 2014-07-07T16:29:46.833 回答
1

在我的情况下,原因是标题之后和请求正文之前的额外换行符。我猜有 Content-Length 不一致会抛出解析器。如果您以编程方式设置标题,请确保它们没有尾随换行符。

于 2013-05-02T20:45:02.467 回答