如何在Rails rescue_from语句中重新引发Ruby异常?

时间:2016-05-18 03:22:44

标签: ruby-on-rails pundit rescue rocketpants

My Rails 4应用程序使用RocketPants作为其JSON API和Pundit进行授权。

我的/app/controllers/api/v1/base_controller.rb文件中有代码来处理Pundit的错误。每当用户未被授权更新资源时,Pundit会抛出NotAuthorizedError异常并使用我的user_not_authorized方法进行救援:

class API::V1::BaseController < RocketPants::Base
  include Pundit
  version 1

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  def user_not_authorized
    error! :forbidden
  end

end

当我从异常处理程序中调用error! RocketPants provides方法时,我希望得到这样的JSON响应:

{
  "error":             "forbidden",
  "error_description": "The requested action was forbidden."
}
但是,相反,调用error!会立即炸毁请求:

Completed 500 Internal Server Error in 143ms

RocketPants::Forbidden - RocketPants::Forbidden:
  rocket_pants (1.13.1) lib/rocket_pants/controller/error_handling.rb:44:in `error!'
  app/controllers/api/v1/base_controller.rb:61:in `user_not_authorized'

完整堆栈跟踪here

为什么error!方法在从我的Pundit异常处理程序调用时应该做什么呢?

如果我将error! :forbidden放在我的控制器操作中间,它会按预期工作。

对于上下文,继承自base_controller.rb并调用Pundit的authorize方法的控制器如下所示:

class API::V1::MealsController < API::V1::BaseController

  before_filter :find_entity

  def create
    meal = @entity.meals.build(meal_params)

    authorize(@entity, :update?)

    if meal.save
      expose meal, status: :created
    else
      expose meal.errors, status: 422
    end
  end

end

1 个答案:

答案 0 :(得分:1)

显然在rescue_from中引发异常是一个坏主意,根据Rails文档,处理程序中引发的异常不会冒泡:

  

异常处理程序中引发的异常不会传播。

文档:http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html

我没有重新提升RocketPants的异常,而只是自己创建并返回JSON错误消息:

  def user_not_authorized
    # error! :forbidden
    head 403
    error = { error: 'Action not allowed.', error_description: 'Sorry, you are not allowed to perform this action.'}
    expose error
  end

这个有效!

<强>更新

我已经找到了一个更清晰的解决方案:只需映射 Pocket的例外RocketPants例外。这意味着每当出现Pundit::NotAuthorizedError错误时,它都会被视为RocketPants::Forbidden错误。

将整个解决方案归结为base_controller.rb顶部的一行代码:

  map_error! Pundit::NotAuthorizedError, RocketPants::Forbidden

不需要处理程序。