ruby Maven:如何处理一个未经授权的操作的多个错误代码?

r55awzrz  于 2023-04-05  发布在  Ruby
关注(0)|答案(2)|浏览(172)

我使用pundit来处理我的API策略,我有一个项目显示,可以在某些情况下禁止用户,在其他情况下只是限制。通过限制我的意思是它现在被禁止,但如果他支付,他可以访问它。所以我需要我的API响应一个特定的代码(402 Payment Required),这样客户端就可以邀请用户支付,以解锁显示。
这是我目前的代码,当pundit返回false时,它只响应403
在哪里实现一个条件返回403402以获得DRY和清洁是最好的?

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
vs91vp4v

vs91vp4v1#

不幸的是,Pundit不能处理不同的错误类型。并且它被构建为总是期望策略的方法返回true或false 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的一部分,在这种情况下,使用这个全局数据存储避免了覆盖pundit内部。
您可能需要阅读有关CurrentAttributes的API文档。

wtzytmuj

wtzytmuj2#

在app/controllers/concerns/response.rb中创建Response模块

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

在app/controllers/concerns/exception_handler.rb中创建ExceptionHandler

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

在app/controllers/application_controller.rb中

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

就是这样

相关问题