我有一个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是空的。
之前的感觉就像一个黑客。无论怎样,我都希望这些参数进来......我必须在那里做错事但我不知道它是什么。
任何帮助都会深深感激......
答案 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