Sinatra控制器参数方法在JSON post请求时变为空

时间:2012-08-26 16:23:37

标签: json sinatra

我有一个Sinatra应用程序,在我的大多数控制器中,json进入并在params对象中自动拾取。但是,除非我使用before方法来拉取request.body参数将它们解析为JSON并将它们合并到params散列中,否则我的后期操作根本无法获得参数。

这是控制器,以及过滤方法:

before do
  if request.request_method == "POST"
    body_parameters = request.body.read
    params.merge!(JSON.parse(body_parameters))
  end
end


post '/locations/new' do
  content_type :json
  puts "params after post params method = #{params.inspect}"
  ... other code ...
end

我看到的输出基本上是控制器动作中的参数实际上正确存在。但是,如果我在调用之前注释掉params是空的。

之前的感觉就像一个黑客。无论怎样,我都希望这些参数进来......我必须在那里做错事但我不知道它是什么。

任何帮助都会深深感激......

3 个答案:

答案 0 :(得分:33)

为了回答这个问题,我们首先要看一些HTTP请求(只不过是简单的telnet'消息';这可以很容易地手工重新创建)。首先,当您提交普通HTML <form>时会发生什么? POST请求看起来与此类似(可能有一些额外的参数,但我们现在不需要担心):

POST /submit-form HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 12

name=JohnDoe

逐个字符地键入(将/sample-form替换为任何表单操作的URL,以及带有您的IP或主机名的Host)将与浏览器发送的内容相同。从中学到的重要一点是参数语法:formname=formvalue Sinatra使用此语法将POST请求的正文解释为params哈希!因此,JSON请求与此基本不兼容,因此出现在params哈希中。

但是,您在before区块中执行的操作会显示正确的解决方案。虽然以上params{'name' => 'JohnDoe'},但request.body.read将返回原始正文name=JohnDoe

知道这一点后,人们可以理解为什么你的'hacky'解决方案有效:POST请求的原始主体由JSON.parse解释,然后插入空params哈希。它似乎很丑陋的原因是因为params在这个例子中是一个不必要的中间人。以下应该做的工作:

post '/locations/new' do
    @json = JSON.parse(request.body.read)
    # @json now contains a hash of the submitted JSON content
end

但是,执行更好实践的解决方案要么仅在提供JSON内容时作出响应,要么在提交标准表单时作出不同响应。如上面的示例HTTP POST请求所示,HTML表单标识为application/x-www-form-urlencoded MIME类型,而JSON标识为application/json。如果您想查看POST请求的MIME类型的详细信息,请查看this question with some great answers如何使用Sinatra执行此操作!

答案 1 :(得分:12)

有类似的问题: Posting JSON params from java to sinatra service

我找到了一个更好的解决方案来处理它,通过添加一个中间件来为我做同样的事情。我使用了rack-contrib gem。以下是我在代码中所做的更改:

编辑:使用git获取特定版本,在内容类型为application/json;charset=UTF-8时修复问题

<强>的Gemfile:

gem 'rack-contrib', git: 'git@github.com:rack/rack-contrib', ref: 'b7237381e412852435d87100a37add67b2cfbb63'

<强> config.ru:

use Rack::PostBodyContentTypeParser

来源:http://jaywiggins.com/2010/03/using-rack-middleware-to-parse-json/

答案 2 :(得分:0)

迟到聚会,但是如果有人仍然需要-

要添加到goyalankit的答案中:如果您尝试对此进行测试(例如,使用RSpec),则可能无法正常工作,因为标准测试设置未使用中间件。

还要在测试中使用中间件:

# spec_helper.rb

OUTER_APP = Rack::Builder.parse_file("config.ru").first

module RSpecMixin
  include Rack::Test::Methods
  def app
    OUTER_APP # typically this might just be Sinatra::Application
  end
end

RSpec.configure do |config|
  config.include RSpecMixin
end

示例用法:

it 'is ok' do
  post '/', { key: 'value' }.to_json, { 'CONTENT_TYPE' => 'application/json' }
  expect(last_response).to be_ok
end

还有我的config.ru:

require 'rack/contrib'
require './app'

use Rack::PostBodyContentTypeParser
run Sinatra::Application