我使用pundit处理我的API策略,有一个项目显示在某些情况下可以禁止用户使用,而在其他情况下则仅受限制。受限,我的意思是现在禁止这样做,但是如果他付费,则可以访问它。因此,我需要我的API以特定的代码(402 Payment Required
进行响应,以便客户可以邀请用户付款以解锁节目。
这是我当前的代码,当pundit返回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
答案 0 :(得分:2)
很遗憾,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文档。