如何在 Rails rescue_from 语句中重新引发 Ruby 异常?
How can I re-raise a Ruby exception in a Rails rescue_from statement?
我的 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
当我从我的异常处理程序调用 RocketPants provides 的 error!
方法时,我希望得到这样的 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
显然在 rescue_from
中引发异常是一个坏主意,根据 Rails 文档,处理程序中引发的异常不会冒泡:
Exceptions raised inside exception handlers are not propagated up.
文档: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
这有效!
更新
从那以后我找到了一个更简洁的解决方案:只是 map RocketPants 异常的 Pundit 异常。这意味着每当出现 Pundit::NotAuthorizedError
错误时,它将被视为 RocketPants::Forbidden
错误。
在 base_controller.rb
的顶部将整个解决方案缩减为一行代码:
map_error! Pundit::NotAuthorizedError, RocketPants::Forbidden
不需要处理程序。
我的 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
当我从我的异常处理程序调用 RocketPants provides 的 error!
方法时,我希望得到这样的 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
显然在 rescue_from
中引发异常是一个坏主意,根据 Rails 文档,处理程序中引发的异常不会冒泡:
Exceptions raised inside exception handlers are not propagated up.
文档: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
这有效!
更新
从那以后我找到了一个更简洁的解决方案:只是 map RocketPants 异常的 Pundit 异常。这意味着每当出现 Pundit::NotAuthorizedError
错误时,它将被视为 RocketPants::Forbidden
错误。
在 base_controller.rb
的顶部将整个解决方案缩减为一行代码:
map_error! Pundit::NotAuthorizedError, RocketPants::Forbidden
不需要处理程序。