私は、プレーンな古いRubyオブジェクト(PORO)を使用して、コントローラーから承認ロジックを抽象化するアプリケーションを作成しています。
現在、コントローラレベルでrescue_from
というNotAuthorized
というカスタム例外クラスがありますが、知りたいと思っていました:Does Rails 4 already come この例外を実装することで車輪を再発明していますか?
明確化:raise AuthorizationException
はコントローラーの内部では発生していません。コントローラーの外部で完全に分離されたPOROの内部で発生しています。オブジェクトには、HTTP、ルート、またはコントローラーに関する知識がありません。
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
を使用します。
私は理由を推測していますRailsこの例外を導入しなかったのは、認証と認証がRailsネイティブ動作(もちろん、基本認証を考慮しない)。
通常、これらは他のライブラリの責任です Devise NotAuthenticated; Pundit 、CanCanCan、NotAuthorizedのRollify)実際に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
これは、この例外が発生してキャッチされることを期待している層が、サードパーティのライブラリではなく、アプリケーション層であることを明確に定義しています。
問題は、ライブラリが変更される可能性があることです(そして、これはRailsも意味します)サードパーティのlibクラスで例外を定義し、アプリケーション層でそれらを救済することは、例外クラスの意味が変わるかのように本当に危険です) rescue_from
にブレーキをかけます
Rails raise
-rescue_from
が現代のgoto
であることに注意を払っている多くの記事を読むことができます。専門家)そして特定の拡張ではそれは真実ですが、あなたが完全に制御できない例外を救出している場合にのみ!!
つまり、サードパーティの例外(DeviseおよびRails特定のポイントまで)を含みます。アプリケーションで例外クラスを定義する場合、サードパーティのlibにリレーしていません=>フルコントロール=>これをアンチパターンにせずにrescue_from
できます。