我有一个Sinatra应用程序,并且在我的大多数控制器中,json都会传入并在params对象中自动获取。但是,除非我用before方法来拉取request.body参数,否则我将获得一个根本不获取参数的后期操作.body参数将它们解析为JSON并将它们合并到params哈希中。
这是控制器以及filter方法:
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
我看到的输出基本上是,控制器操作中的参数实际上正确地位于其中。但是,如果我注释掉before调用,则参数为空。
之前本身感觉就像是黑客。我希望这些参数无论如何都会出现…我必须在那里做错什么,但我不知道那是什么。
任何帮助将不胜感激…
为了回答这个问题,我们首先必须查看一些HTTP请求(仅是简单的telnet“消息”;可以很容易地手动创建)。首先,提交普通的HTML会发生什么<form>?该POST请求看起来将与此非常相似(可能带有一些额外的参数,但是我们现在不必担心这一点):
telnet
<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 替换URL,并Host用您的IP或主机名替换该字符)将与您的浏览器发送的内容相同。要学习的重要内容是参数语法:formname=formvalue。 Sinatra 使用此语法将POST请求的内容解释为params哈希!因此,与此基本不兼容的JSON请求将因此 不会 显示在params哈希中。
/sample-form
Host
formname=formvalue
params
但是,您在before街区中所做的事情显示了正确的解决方案。虽然params从上面将是{'name' => 'JohnDoe'},request.body.read将返回原始主体,name=JohnDoe。
before
{'name' => 'JohnDoe'}
request.body.read
name=JohnDoe
知道了这一点,就可以理解为什么您的“ hacky”解决方案起作用了:POST请求的原始主体由解释JSON.parse,然后插入到空params哈希中。看起来很笨拙的原因是因为params在此示例中是不必要的中间人。以下应完成此工作:
JSON.parse
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- urlencodedMIME类型标识,而JSON使用标识application/json。
application/x-www-form- urlencoded
application/json