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

时间:2020-06-23 11:09:56

标签: ruby-on-rails api controller pundit rescue

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

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是我所有麻烦的根源。当控制器错误是唯一发生的情况时,该控制器可以很好地工作并从Pundit白名单检查中恢复。

但是,一旦其他任何错误与专家一起发生,我就会得到DoubleRenderError,因为这两种救援都最终被触发了。我正在寻找一种快速调整措施,以防止在发生另一个错误时才触发专家,或者针对此问题的替代解决方案。

还有其他错误类可以用来避免严重依赖StandardError吗?

我尝试过的事情

  • 在渲染后添加退货 没用我认为救援链会干扰正常的render :x and return行为。

非常感谢您!

2 个答案:

答案 0 :(得分:1)

您实际上并不需要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

答案 1 :(得分:0)

一个简单的快速修复方法是在渲染后仅使用return。这样,下一步将无法运行。因此,在最后的所有方法中,只需使用return

例如:

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

    return
  end