【问题标题】:Sinatra controller params method coming in empty on JSON post requestSinatra 控制器参数方法在 JSON 发布请求中为空
【发布时间】:2021-01-30 05:32:04
【问题描述】:

我有一个 Sinatra 应用程序,在我的大多数控制器中,json 都会进入并在 params 对象中自动获取。但是,我有一个 post 操作根本无法获取参数,除非我使用 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

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

before 本身感觉就像一个 hack。无论如何,我都希望那些参数会进来……我一定在里面做错了什么,但我不知道那是什么。

任何帮助将不胜感激......

【问题讨论】:

    标签: json sinatra


    【解决方案1】:

    为了回答这个问题,我们首先要查看一些 HTTP 请求(这些请求只不过是简单的 telnet 'messages';这可以很容易地手动重新创建)。首先,当你提交一个普通的 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,Host 替换为您的IP 或主机名)将与您的浏览器发送的内容相同。从中学习的重要一点是参数语法:formname=formvalueSinatra 使用此语法将 POST 请求的主体解释为 params 哈希!因此,与此基本不兼容的 JSON 请求将不会显示在因此,params 散列。

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

    知道了这一点,就可以理解为什么你的“hacky”解决方案有效:POST 请求的原始正文由JSON.parse 解释,然后插入到空的params 哈希中。看起来很hacky的原因是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 执行此操作!

    【讨论】:

    【解决方案2】:

    遇到了类似的问题: Posting JSON params from java to sinatra service

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

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

    宝石文件:

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

    config.ru:

    use Rack::PostBodyContentTypeParser
    

    来源:https://www.jaywiggins.com/2010/03/29/Using-Rack-middleware-to-parse-JSON/

    【讨论】:

    • 很遗憾,它没有被列为正确答案,因为它处理接受的答案中的问题,假设为 JSON 正文,如果正文为空,则会引发异常。
    • 源链接已损坏
    【解决方案3】:

    聚会迟到了,但如果还有人需要的话 -

    补充 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
    

    【讨论】:

    • UPDATE Rack::PostBodyContentTypeParser 现在已弃用,因此您应该改为 use Rack::JSONBodyParser(无需其他更改)
    猜你喜欢
    • 2013-11-19
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多