在这种情况下如何避免双重渲染?

How can I avoid double render in this situation?

我的控制器中的这段代码有问题:

class Api::V1::BaseController < ActionController::API
  include Pundit

  after_action :verify_authorized, except: :index
  after_action :verify_policy_scoped, only: :index

  rescue_from StandardError,                with: :internal_server_error
  rescue_from Pundit::NotAuthorizedError,   with: :user_not_authorized
  rescue_from ActiveRecord::RecordNotFound, with: :not_found

  private

  def user_not_authorized(exception)
    render json: {
      error: "Unauthorized #{exception.policy.class.to_s.underscore.camelize}.#{exception.query}"
    }, status: :unauthorized
  end

  def not_found(exception)
    render json: { error: exception.message }, status: :not_found
  end

  def internal_server_error(exception)
    if Rails.env.development?
      response = { type: exception.class.to_s, message: exception.message, backtrace: exception.backtrace }
    else
      response = { error: "Internal Server Error" }
    end
    render json: response, status: :internal_server_error
  end
end

问题

rescue_from StandardError是我所有烦恼的根源。这个控制器工作得很好,当专家错误是唯一发生的时候,从专家白名单检查中拯救出来。

但是,一旦与专家一起发生任何其他错误,我就会得到一个 DoubleRenderError,因为两次救援最终都会被触发。我正在寻找一个快速调整,以避免在另一个错误已经发生时触发专家,或者这个问题的替代解决方案。

是否还有其他错误 class 可以用来避免过度依赖 StandardError

我尝试过的东西

非常感谢!

您实际上不需要 rescue_from StandardError,因为这是 Rails 的默认行为。 Rails 有一个名为 PublicExceptions 的中间件,它(主要)做你想做的,所以你可以让 StandardError 传播。

而不是 { error: "Internal Server Error" } 它将呈现这个

{ 
  status: status, 
  error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) 
}

如果出现异常,将呈现 { status: 500, error: "Internal Server Error" }。这应该是一个合理的妥协。

对于开发,您可以考虑调整此中间件。您可以使用 config.exceptions_app.

进行设置

https://guides.rubyonrails.org/configuring.html#rails-general-configuration

https://api.rubyonrails.org/classes/ActionDispatch/PublicExceptions.html

https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/public_exceptions.rb#L14

一个简单的快速修复方法是在渲染后使用 return。这样接下来就不会 运行 了。因此,在最后的所有方法中,只需使用 return:

例如:

  def user_not_authorized(exception)
    render json: {
      error: "Unauthorized #{exception.policy.class.to_s.underscore.camelize}.#{exception.query}"
   }, status: :unauthorized

    return
  end