我写了一段Rack Middleware来自动解压缩压缩的请求体.代码似乎工作得很好,但是当我将它插入我的rails应用程序时,我从ActionController :: ParamsParser获得了“Invalid
JSON”失败.
作为一种调试机制,我正在将压缩内容和解压缩的内容写入文件(以确保代码正常工作),并且我确实收到了原始的JSON文档(在客户端将其拉出之前).
我发布的数据是JSON数据,解压缩的内容从http://jsonlint.com检测为有效JSON.
我有什么想法我做错了吗?
class CompressedRequests def initialize(app) @app = app end def call(env) input = env['rack.input'].read #output the zipped data we received File.open('/Users/ben/Desktop/data.gz','w+') do |f| f.write input end if env['REQUEST_METHOD'] =~ /(POST|PUT)/ if env.keys.include? 'HTTP_CONTENT_ENCODING' new_input = decode(input,env['HTTP_CONTENT_ENCODING']) env['rack.input'] = StringIO.new(new_input) #output our decoded data (for debugging) File.open('/Users/ben/Desktop/data.txt','w+') do |f| f.write env['rack.input'].read end env.delete('HTTP_CONTENT_ENCODING') end end env['rack.input'].rewind status,headers,response = @app.call(env) return [status,response] end def decode(input,content_encoding) if content_encoding == 'gzip' Zlib::GzipReader.new(input).read elsif content_encoding == 'deflate' Zlib::Inflate.new.inflate new input else input end end end
这是我从控制台得到的错误:
Contents::"2010-05-17T12:46:30Z","background":false},{"longitude":-95.38620785000001,"latitude":29.62815358333334,"estimated_speed":14.04305,"timestamp":"2010-05-17T12:46:36Z",{"longitude":-95.3862767,"latitude":29.62926725,"estimated_speed":39.87791,"timestamp":"2010-05-17T12:46:42Z",{"longitude":-95.38655023333334,"latitude":29.63051011666666,"estimated_speed":46.09239,"timestamp":"2010-05-17T12:46:49Z",{"longitude":-95.38676226666666,"latitude":29.63158775,"estimated_speed":47.34936,"timestamp":"2010-05-17T12:46:55Z",{"longitude":-95.38675346666666,"latitude":29.63219841666666,"estimated_speed":22.54016,"timestamp":"2010-05-17T12:47:03Z",{"longitude":-95.38675491666666,"latitude":29.63265714999999,"estimated_speed":14.03642,"timestamp":"2010-05-17T12:47:10Z",{"longitude":-95.38677551666666,"latitude":29.63358661666667,"estimated_speed":29.29489,"timestamp":"2010-05-17T12:47:17Z",{"longitude":-95.38679026666662,"latitude":29.63466445,"estimated_speed":38.34926,"timestamp":"2010-05-17T12:47:24Z",{"longitude":-95.38681656666668,"latitude":29.63590941666666,"estimated_speed":44.82093,"timestamp":"2010-05-17T12:47:31Z",{"longitude":-95.38683366666667,"latitude":29.63679638333334,"estimated_speed":40.21729,"timestamp":"2010-05-17T12:47:37Z",{"longitude":-95.38685133333333,"latitude":29.63815714999999,"estimated_speed":44.86543,"timestamp":"2010-05-17T12:47:44Z",{"longitude":-95.3868655 /!\ FAILSAFE /!\ Mon Oct 18 18:18:43 -0500 2010 Status: 500 Internal Server Error Invalid JSON string /Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/json/backends/yaml.rb:14:in `decode' /Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/json/decoding.rb:11:in `__send__' /Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/json/decoding.rb:11:in `decode' /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/params_parser.rb:42:in `parse_formatted_parameters' /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/params_parser.rb:11:in `call' /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/session/cookie_store.rb:93:in `call' /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/failsafe.rb:26:in `call' /Users/ben/projects/safecell/safecellweb/lib/compressed_requests.rb:36:in `call' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `call' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `synchronize' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `call' /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/dispatcher.rb:114:in `call' /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/reloader.rb:34:in `run' /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/dispatcher.rb:108:in `call' /Library/Ruby/Gems/1.8/gems/rails-2.3.5/lib/rails/rack/static.rb:31:in `call' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:46:in `call' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:40:in `each' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:40:in `call' /Library/Ruby/Gems/1.8/gems/rails-2.3.5/lib/rails/rack/log_tailer.rb:17:in `call' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/content_length.rb:13:in `call' /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/handler/webrick.rb:50:in `service'
最后一条信息,我在ActionController :: Failsafe之后插入这个中间件.
编辑:看起来这不是截断问题
经过多次挖掘,看起来它毕竟不是截断问题.日志只是剪切输出,因此它看起来像截断问题.
此时我不确定为什么JSON无效.我是否需要进行任何手动转义?
解决方法
我不是任何一个ruby专家.我也没有试图重新解决这个问题,以验证我的结果.但是在挖掘机架和actionpack代码之后,我可能会有所收获.
“rack.input”的文档指出:
“输入流是一个类似IO的对象,它包含原始的HTTP POST数据.”
所以你看起来正确使用它.
但是,actionpack尝试将JSON解析出正文(如果内容类型指定为JSON)并检索正文,如下所示:
when :json body = request.raw_post
其中“request”是actionpack自己的Request类,“raw_post”的定义如下:
def raw_post unless @env.include? 'RAW_POST_DATA' @env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i) body.rewind if body.respond_to?(:rewind) end @env['RAW_POST_DATA'] end
和“Request.body”是:
def body if raw_post = @env['RAW_POST_DATA'] raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) StringIO.new(raw_post) else @env['rack.input'] end end
这一切看起来都很好(虽然很难弄清楚谁先缓存价值:)).
看起来问题在于如何读取帖子数据:
@env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i)
所以我猜测问题是因为你更改了“rack.input”但没有更新“CONTENT_LENGTH”,所以actionpack会截断数据,因为很明显,压缩内容会比解压缩的内容短.
尝试更新中间件代码中的“CONTENT_LENGTH”,看看是否能修复它.