权威人士:如何处理一个未经授权的操作的多个错误代码



我使用权威人士来处理我的API策略,我有一个项目显示,在某些情况下可以禁止用户使用,而在其他情况下只是受到限制。我所说的限制是指现在禁止它,但如果他付钱,他可以访问它。所以我需要我的API响应特定的代码(402 Payment Required(,以便客户端可以邀请用户付款以解锁节目。

这是我当前的代码,它仅在权威人士返回 false 时响应403

为了干燥和清洁,最好在哪里实施返回403402的条件?

class Api::V1::ItemController < Api::V1::BaseController
  def show
    @item = Item.find(params[:id])
    authorize @item
  end
end
class ItemPolicy < ApplicationPolicy
  def show?
    return true if record.public?
    # 403 will be generated, that's ok.
    return false if !record.band.members.include?(user)
    # If that condition is false I want to generate a 402 error at the end, not a 403.
    user.premium?
  end
end
class Api::V1::BaseController < ActionController::API
  include Pundit
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
  def user_not_authorized(_exception)
    # Here I've got the exception with :policy, :record and :query, 
    # also I can access :current_user so I could go for a condition, 
    # but that would include duplicated code from  ItemPolicy#show?.
    render json: { error: { message: "Access denied" } }, status: :forbidden
  end
end

不幸的是,Pundit无法开箱即用地处理不同的错误类型。并且它始终期望策略的方法返回true或错误false。因此,引发另一个自定义错误并从控制器中拯救将不起作用,因为它也会破坏视图方法。

我建议一种解决方法来引入不同的错误类型。像这样的东西可能会起作用:

# in the policy
class ItemPolicy < ApplicationPolicy
  def show?
    return true if record.public?
    return false unless record.band.members.include?(user)
    if user.premium?
      true
    else
      Current.specific_response_error_code = :payment_required
      false
    end
  end
end
# in the controller
class Api::V1::BaseController < ActionController::API
  include Pundit
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
  def user_not_authorized(_exception)
    case Current.specific_response_error_code
    when :payment_required
      render json: { error: { message: "Premium required" } }, status: :payment_required
    else
      render json: { error: { message: "Access denied" } }, status: :forbidden
    end
  end
end

我不认为使用全局CurrentAttributes是一种好的做法,但它们是 Rails 的一部分,在这种情况下,使用此全局数据存储可以避免覆盖权威人士的内部结构。

您可能需要阅读有关 CurrentAttributes 的 API 文档。

在app/controllers/concern/response.rb中创建响应模块

module Response
  def json_response(object, status = :ok)
    render json: object, status: status
  end
end

在应用程序/控制器/关注点/exception_handler.rb 中创建异常处理程序

module ExceptionHandler
  extend ActiveSupport::Concern
  included do
    rescue_from Pundit::NotAuthorizedError, with: :unauthorized_request
  end
  private
  # JSON response with message; Status code 401 - Unauthorized
  def unauthorized_request(e)
    json_response({ message: e.message }, :unauthorized)
  end
end

在应用程序/控制器/application_controller.rb 中

class ApplicationController < ActionController::API
   include Response
   include ExceptionHandler
end

就是这样

最新更新