Rails是否带有"未授权"的例外?

Ric*_*ick 32 ruby ruby-on-rails actioncontroller

我正在编写一个使用普通旧Ruby对象(PO​​RO)从控制器中抽象出授权逻辑的应用程序.

目前,我有调用自定义异常类NotAuthorized,我rescue_from在控制器级别,但我很好奇,想知道:是否导轨4已经配备了一个例外,表明一个动作没有被授权?我是通过实施这个例外来重新发明轮子的吗?

澄清:raise AuthorizationException控制器内部没有发生任何事情,它发生在控制器外部完全分离的PORO内部.该对象不了解HTTP,路由或控制器.

Ste*_*fan 32

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
)
Run Code Online (Sandbox Code Playgroud)

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
)
Run Code Online (Sandbox Code Playgroud)

您可以在应用程序的配置(或自定义Railtie)中添加自定义异常:

Your::Application.configure do

  config.action_dispatch.rescue_responses.merge!(
    'AuthorizationException' => :unauthorized
  )

  # ...

end
Run Code Online (Sandbox Code Playgroud)

或者只是使用rescue_from.

  • @Rick https://github.com/mongoid/mongoid/blob/master/lib/mongoid/railtie.rb#L37 (2认同)

equ*_*nt8 22

我猜测Rails没有引入此异常的原因是因为授权和身份验证不是Rails本机行为(当然不考虑basicauth).

通常这些是其他图书馆的责任设计 Not Notheutheated; Pundit,CanCanCan,Rollify for 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
Run Code Online (Sandbox Code Playgroud)

因此在我的控制器中我能做到

class MyStuff < ApplicationController
  def index
    if current_user.admin?
      # ....
    else 
      raise ApplicationController::NotAuthorized
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

这清楚地定义了您期望引发此异常的层是您的应用层,而不是第三方库.

问题是库可以改变(是的,这也就是Rails)在第三方lib类上定义异常并在应用程序层中拯救它们是非常危险的,好像异常类的含义改变它会制动你的 rescue_from

你可以阅读很多人们关注Rails的文章raise- rescue_from现代goto(现在正在考虑一些专家的反模式)并且在某种程度上它是真的,但只有当你正在拯救例外时你没有完全控制权!

这意味着第三方例外(包括Devise和Rails到某一点).如果你在你的应用程序中定义了异常类,你就不会在第三方lib =>你完全控制=>你rescue_from没有这是一个反模式.