【问题标题】:Overriding the default Content-Type覆盖默认的 Content-Type
【发布时间】:2018-05-29 22:22:27
【问题描述】:

我有一个 Rails API,它只接受 JSON 作为输入。如果我未能包含 Content-Type: application/json 的标头,则 request.headers['Content-Type'] 默认为 application/x-www-form-urlencoded 并且参数无法正确解析。整个 json 主体成为参数中的一个键。结果是 422,这让 API 用户感到困惑。

如果没有提供 Content-Type 标头,如何将其更改为默认解析为 json?

许多其他问题回答了如何使用响应格式执行此操作。要更改此默认值,您可以在控制器中指定它:

 request.format = :json

或者在路由命名空间中,例如:

 namespace :api, defaults: {format: :json} do

但是,这会更改默认响应格式,但不会更改默认请求格式。 我需要做的是更改解析参数的默认请求格式。

【问题讨论】:

    标签: ruby-on-rails json


    【解决方案1】:
    parsed = JSON.parse(json_body) unless request.headers["Content-Type"] == 'application/json'
    

    【讨论】:

    • 显示的代码将生成一个哈希,而不是标准使用所需的 ActionController::Parameters 实例。我可以使用以下变体来获得我想要的东西,但是我必须将它粘贴到每个控制器方法中,因为我无法从 Rails 修改默认的params。 params = ActionController::Parameters.new(JSON.parse(request.body.read)) if request.headers["Content-Type"]=="application/x-www-form-urlencoded" 一定有更简单的方法!
    • 也许在参数解析之前在机架上插入一些中间件,如果标题错误,则正确解析它们?也许这可以帮助你content.pivotal.io/blog/…(也许有更简单的方法,不确定,虽然这看起来很简单)
    • 似乎在 Rails 5 中不再使用 Rack 中间件来解析参数,因此关键的博客解决方案不再有效。 github.com/rails/rails/commit/…
    • @davidgyoung 似乎是这样 - 这很不幸。好吧,您应该能够在分派到路由之前将其插入机架(如果您这样做rake middleware,它将向您显示所有中间件从头到尾),尽管我不知道您如何更改 params 对象并拥有在通过机架发送请求时保留该更改。但是,与此同时,我认为您的用户应该明确地将正确的标头传递给请求,因为这就是请求的格式。
    • 是的,我同意他们应该传递标题,但这是一个容易犯的错误,所以我试图让它对用户友好。当前的 Rails 默认设置似乎随意且糟糕。另一种解决方案可能是在 JSON 响应中发送自定义错误消息,告诉用户设置 ContentType: application/json
    【解决方案2】:

    这是我公认的可怕解决方案,源自 Micael Nussbaumer 的回答中的建议。如果一些 Rubyist 能神奇地将这个丑陋的 hack 变成一个简洁的单行线,我会很高兴的。

    module Api
      class BaseApiController < ActionController::API
    
      private
        # This is an ugly hack needed to make it default to json if you do not
        # specify a Content-Type.  If you see this and know of a better way please
        # say so!
        def params
          if !@params
            if request.headers["Content-Type"]=="application/x-www-form-urlencoded"
              body_string = request.body.read
              begin
                hash = JSON.parse(body_string)
                @params = ActionController::Parameters.new(hash)
              rescue
                # do nothing
              end
            end
            if !@params
              @params = super
            end
          end
          @params
        end
    
        ...
    
    end
    

    【讨论】:

      【解决方案3】:

      我已经以这种方式为 Rails API 使用中间件解决了这个问题 (rails new my_project --api)

      配置:

      # config/application.rb
      # ...
      require './lib/middleware/consider_all_request_json_middleware'
      # ...
      
      module MyApplication
          # ...
        class Application < Rails::Application
              # ...
              config.middleware.insert_before(ActionDispatch::Static,ConsiderAllRequestJsonMiddleware)
              # ...
      

      中间件:

      # lib/middleware/consider_all_request_json_middleware.rb
      class ConsiderAllRequestJsonMiddleware
        def initialize app
          @app = app
        end
      
        def call(env)
          if env["CONTENT_TYPE"] == 'application/x-www-form-urlencoded'
            env["CONTENT_TYPE"] = 'application/json'
          end
          @app.call(env)
        end
      end
      

      原文:https://blog.eq8.eu/til/content-type-applicationjson-by-default-in-rails-5.html

      【讨论】:

        猜你喜欢
        • 2015-10-27
        • 2023-03-27
        • 1970-01-01
        • 1970-01-01
        • 2015-12-28
        • 1970-01-01
        • 2023-03-21
        • 1970-01-01
        • 2012-03-15
        相关资源
        最近更新 更多