我使用权威人士来处理我的API策略,我有一个项目显示,在某些情况下可以禁止用户使用,而在其他情况下只是受到限制。我所说的限制是指现在禁止它,但如果他付钱,他可以访问它。所以我需要我的API响应特定的代码(402 Payment Required
(,以便客户端可以邀请用户付款以解锁节目。
这是我当前的代码,它仅在权威人士返回 false 时响应403
。
为了干燥和清洁,最好在哪里实施返回403
或402
的条件?
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
就是这样