Имеет ли Rails "исключенное" исключение?
Я пишу приложение, которое использует простые старые объекты Ruby (POROs) для абстрактной логики авторизации из контроллеров.
В настоящее время у меня есть собственный класс исключений, называемый NotAuthorized
который я rescue_from
на уровне контроллера, но мне было любопытно узнать: появилось ли в Rails 4 исключение, указывающее, что действие не было санкционировано? Я заново изобретаю колесо, выполняя это исключение?
Уточнение: raise AuthorizationException
не происходит нигде внутри контроллера, оно происходит внутри полностью развязанного PORO вне контроллера. Объект не знает HTTP, маршрутов или контроллеров.
Ответы
Ответ 1
Rails, похоже, не отображает исключение :unauthorized
.
Сопоставления по умолчанию определяются в activerecord/lib/active_record/railtie.rb:
config.action_dispatch.rescue_responses.merge!(
'ActiveRecord::RecordNotFound' => :not_found,
'ActiveRecord::StaleObjectError' => :conflict,
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
'ActiveRecord::RecordNotSaved' => :unprocessable_entity
)
и actionpack/lib/action_dispatch/middleware/exception_wrapper.rb:
@@rescue_responses.merge!(
'ActionController::RoutingError' => :not_found,
'AbstractController::ActionNotFound' => :not_found,
'ActionController::MethodNotAllowed' => :method_not_allowed,
'ActionController::UnknownHttpMethod' => :method_not_allowed,
'ActionController::NotImplemented' => :not_implemented,
'ActionController::UnknownFormat' => :not_acceptable,
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
'ActionDispatch::ParamsParser::ParseError' => :bad_request,
'ActionController::BadRequest' => :bad_request,
'ActionController::ParameterMissing' => :bad_request
)
Вы можете добавить настраиваемое исключение из вашей конфигурации приложения (или пользовательской версии Railtie):
Your::Application.configure do
config.action_dispatch.rescue_responses.merge!(
'AuthorizationException' => :unauthorized
)
# ...
end
Или просто используйте rescue_from
.
Ответ 2
Я предполагаю, что Rails не представила это исключение, потому что авторизация и аутентификация не являются собственными поведением Rails (не считая basicauth, конечно).
Обычно это обязанности других библиотек Devise for NotAuthenticated; Pundit, CanCanCan, Rollify для NotAuthorized) Я бы на самом деле утверждал, что может быть неплохо расширить ActionController
с пользовательскими исключениями, такими как ActionController::NotAuthorized
(потому что, как я уже сказал, это не ответственность)
Итак, как я обычно занимаюсь этой проблемой, я ввел пользовательские исключения на ApplicationController
class ApplicationController < ActionController::Base
NotAuthorized = Class.new(StandardError)
# ...or if you really want it to be ActionController
# NotAuthorized = Class.new(ActionController::RoutingError)
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
Это четко определяет, что слой, ожидающий, что это исключение будет поднято и поймано, - это ваш прикладной уровень, а не сторонний lib.
Дело в том, что библиотеки могут меняться (и да, это означает, что Rails тоже), определяющие исключение на сторонних классах lib и спасая их на вашем прикладном уровне, действительно опасны, как если бы значение класса исключений rescue_from
его, это тормозит ваш rescue_from
Вы можете прочитать много статей, в которых люди Waring о Rails raise
- rescue_from
являются современными goto
(теперь рассматривают анти-шаблон среди некоторых экспертов), и в определенной степени это правда, но только если вы спасаете Исключения, которые у вас нет полный контроль off !!
Это означает, что сторонние исключения (включая Devise и Rails до определенной точки). Если вы определяете классы исключений в своем приложении, вы не ретранслируете на третьей стороне lib => у вас есть полный контроль => вы можете rescue_from
без этого анти-шаблона.