简体   繁体   English

如何从Rails 4中的ActionDispatch :: ParamsParser :: ParseError中解救

[英]How to Rescue from ActionDispatch::ParamsParser::ParseError in Rails 4

Rails 4 adds an exception ActionDispatch::ParamsParser::ParseError exception but since its in the middleware stack it appears it can't be rescued in the normal controller environment. Rails 4添加了一个异常ActionDispatch :: ParamsParser :: ParseError异常,但由于它在中间件堆栈中,它似乎无法在普通控制器环境中获救。 In a json API application I want respond with a standard error format. 在json API应用程序中,我想用标准错误格式进行响应。

This gist shows a strategy for inserting middleware to intercept and respond. 这个要点显示了插入中间件以拦截和响应的策略。 Following this pattern I have: 遵循这种模式我有:

application.rb: application.rb中:

module Traphos
  class Application < Rails::Application
    ....
    config.middleware.insert_before ActionDispatch::ParamsParser, "JSONParseError"
 end
end

And the middleware is: 中间件是:

class JSONParseError
  def initialize(app)
    @app = app
  end

  def call(env)
    begin
      @app.call(env)
    rescue ActionDispatch::ParamsParser::ParseError => e
      [422, {}, ['Parse Error']]
    end
  end
end

If I run my test without the middleware I get (spec): 如果我在没有中间件的情况下运行测试,我会得到(spec):

Failures:

  1) Photo update attributes with non-parseable json
     Failure/Error: patch update_url, {:description => description}, "CONTENT_TYPE" => content_type, "HTTP_ACCEPT" => accepts, "HTTP_AUTHORIZATION" => @auth
     ActionDispatch::ParamsParser::ParseError:
       399: unexpected token at 'description=Test+New+Description]'

Which is exactly what I would expect (ParseError that I can't rescue_from). 这正是我所期望的(ParseError,我无法rescue_from)。

Now with the only change to add in the middleware above: 现在只需在上面的中间件中添加更改:

  2) Photo update attributes with non-parseable json
     Failure/Error: response.status.should eql(422)

       expected: 422
            got: 200

And the log shows that the standard controller action is being executed and returning a normal response (although since it didn't receive any parameters it didn't update anything). 并且日志显示正在执行标准控制器操作并返回正常响应(尽管因为它没有接收任何参数而没有更新任何内容)。

My questions: 我的问题:

  1. How can rescue from ParseError and return a custom response. 如何从ParseError中解救并返回自定义响应。 Feels like I'm on the right track but not quite there. 感觉就像我在正确的轨道,但不是那里。

  2. I can't work out why, when the exception is raised and rescued, that the controller action still proceeds. 我无法弄清楚为什么,当异常被提出并获救时,控制器动作仍然继续进行。

Help much appreciated, --Kip 非常感谢, - Kip

Turns out that further up the middleware stack, ActionDispatch::ShowExceptions can be configured with an exceptions app. 事实证明,在中间件堆栈中,可以使用例外应用程序配置ActionDispatch :: ShowExceptions

module Traphos
  class Application < Rails::Application
    # For the exceptions app
    require "#{config.root}/lib/exceptions/public_exceptions"
    config.exceptions_app = Traphos::PublicExceptions.new(Rails.public_path)
  end
end

Based heavily on the Rails provided one I am now using: 基于Rails,我现在使用的是:

module Traphos
  class PublicExceptions
    attr_accessor :public_path

    def initialize(public_path)
      @public_path = public_path
    end

    def call(env)
      exception    = env["action_dispatch.exception"]
      status       = code_from_exception(env["PATH_INFO"][1..-1], exception)
      request      = ActionDispatch::Request.new(env)
      content_type = request.formats.first
      body         = {:status => { :code => status, :exception => exception.class.name, :message => exception.message }}
      render(status, content_type, body)
    end

    private

    def render(status, content_type, body)
      format = content_type && "to_#{content_type.to_sym}"
      if format && body.respond_to?(format)
        render_format(status, content_type, body.public_send(format))
      else
        render_html(status)
      end
    end

    def render_format(status, content_type, body)
      [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
                'Content-Length' => body.bytesize.to_s}, [body]]
    end

    def render_html(status)
      found = false
      path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
      path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))

      if found || File.exist?(path)
        render_format(status, 'text/html', File.read(path))
      else
        [404, { "X-Cascade" => "pass" }, []]
      end
    end

    def code_from_exception(status, exception)
      case exception
      when ActionDispatch::ParamsParser::ParseError
        "422"
      else
        status
      end
    end
  end
end

To use it in a test environment requires setting config variables (otherwise you get the standard exception handling in development and test). 要在测试环境中使用它,需要设置配置变量(否则您将在开发和测试中获得标准异常处理)。 So to test I have (edited to just have the key parts): 所以测试我有(编辑到只有关键部分):

describe Photo, :type => :api do
  context 'update' do
    it 'attributes with non-parseable json' do 

      Rails.application.config.consider_all_requests_local = false
      Rails.application.config.action_dispatch.show_exceptions = true

      patch update_url, {:description => description}
      response.status.should eql(422)
      result = JSON.parse(response.body)
      result['status']['exception'].should match(/ParseError/)

      Rails.application.config.consider_all_requests_local = true
      Rails.application.config.action_dispatch.show_exceptions = false
    end
  end
end

Which performs as I need in a public API way and is adaptable for any other exceptions I may choose to customise. 它以我需要的公共API方式执行,并且适用于我可能选择自定义的任何其他异常。

This article (also from 2013) thoughtbot covers also this topic. 这篇文章(也是从2013年开始) 思想机构也涵盖了这一主题。 They put their response inside this middleware service only if you requested json 只有在您请求json时,他们才会将响应放在此中间件服务中

if env['HTTP_ACCEPT'] =~ /application\/json/
    error_output = "There was a problem in the JSON you submitted: #{error}"
    return [
      400, { "Content-Type" => "application/json" },
      [ { status: 400, error: error_output }.to_json ]
    ]
else
 raise error
end

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 Rails 5 中拯救 ActionDispatch::ParamsParser::ParseError 并返回自定义 API 错误? - How to rescue ActionDispatch::ParamsParser::ParseError and return custom API error in rails 5? 如何在Rails Api中重新指定ActionDispatch :: ParamsParser :: ParseError - How to rspec ActionDispatch::ParamsParser::ParseError in Rails Api Rails-如何拒绝格式错误的参数-ActionDispatch :: ParamsParser :: ParseError - Rails - How to refuse bad formatted params - ActionDispatch::ParamsParser::ParseError Rails服务器使用错误代码795继续响应“ ActionDispatch :: ParamsParser :: ParseError” - Rails server keeps responding with “ActionDispatch::ParamsParser::ParseError” with error code 795 无法在 Rails 6 上拯救 ActionDispatch::Http::Parameters::ParseError - Can not rescue ActionDispatch::Http::Parameters::ParseError on Rails 6 如何在Rails 3.1中初始化ActionDispatch :: ParamsParser? - How do I initialize ActionDispatch::ParamsParser in Rails 3.1? 升级到 Rails 5 后的 ActionDispatch::ParamsParser 替换 - ActionDispatch::ParamsParser replacement after upgrade to Rails 5 如何从ActionDispatch :: Cookie :: CookieOverflow中抢救? - How to rescue_from ActionDispatch::Cookie::CookieOverflow? 如何从特定错误中解救(Ruby on Rails) - How to rescue from a specific error (Ruby on Rails) 如何从 Rails 5 中的 AbstractController::ActionNotFound 异常中解救出来? - how to rescue from AbstractController::ActionNotFound exception in Rails 5?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM