我猜Rails没有引入这个异常的原因是因为授权和认证不是Rails的本机行为(当然,不考虑基本身份验证)。
通常这些是其他库的职责 Devise 用于未经身份验证; Pundit, Dude Policy, CanCanCan, Rollify 用于未经授权)。实际上,我认为将 ActionController
扩展为自定义异常如 ActionController::NotAuthorized
可能是一件坏事(因为就像我说的那样,它不是其职责)。
因此,我通常解决这个问题的方法是在 ApplicationController
上引入自定义异常。
class ApplicationController < ActionController::Base
NotAuthorized = Class.new(StandardError)
rescue_from ActiveRecord::RecordNotFound do |exception|
render_error_page(status: 404, text: 'Not found')
end
rescue_from ApplicationController::NotAuthorized do |exception|
render_error_page(status: 403, text: 'Forbidden')
end
private
def render_error_page(status:, text:, template: 'errors/routing')
respond_to do |format|
format.json { render json: {errors: [message: "#{status} #{text}"]}, status: status }
format.html { render template: template, status: status, layout: false }
format.any { head status }
end
end
end
因此,在我的控制器中,我可以这样做。
class MyStuff < ApplicationController
def index
if current_user.admin?
else
raise ApplicationController::NotAuthorized
end
end
end
这清楚地定义了你期望引发和捕获异常的层是你的应用程序层,而不是第三方库。
问题在于库可能会发生变化(是的,这也包括Rails),在第三方库类上定义异常并在应用程序层中进行救援真的很危险,因为如果异常类的含义发生变化,它会破坏你的
rescue_from
。
你可以阅读许多文章,人们警告Rails
raise
-
rescue_from
成为现代
goto
(现在在某些专家中被认为是反模式),在某种程度上是正确的,但只有当你救援你无法完全控制的异常时才是如此!!
这意味着第三方异常(包括Devise和Rails到一定程度)。如果你在应用程序中定义异常类,你就不依赖第三方库,你就有完全的控制权,你可以
rescue_from
而不会成为反模式。
raise ActionController::RoutingError.new('Not Found')
的东西,它会自动强制应用程序呈现404而不需要任何救援语句。 - BroiSatse