【问题标题】:How to solve NoMethodError with Pundit如何使用 Pundit 解决 NoMethodError
【发布时间】:2018-10-10 13:36:36
【问题描述】:

我不知道我是否在这里做错了,但似乎是这样。

我使用 Pundit 进行授权,我现在已经使用它设置了一些模型。

我有一个只能由管理员创建的类别模型。此外,我也不希望用户看到显示/编辑/销毁视图。我只是希望管理员可以访问它。到目前为止一切顺利。

下面会添加一些代码:

category_policy.rb

class CategoryPolicy < ApplicationPolicy
  def index?
    user.admin?
  end

  def create?
    user.admin?
  end

  def show?
    user.admin?
  end

  def new?
    user.admin?
  end

  def update?
    return true if user.admin?
  end

  def destroy?
    return true if user.admin?
  end
end

categories_controller.rb

class CategoriesController < ApplicationController
  layout 'scaffold'

  before_action :set_category, only: %i[show edit update destroy]

  # GET /categories
  def index
    @category = Category.all
    authorize @category
  end

  # GET /categories/1
  def show
    @category = Category.find(params[:id])

    authorize @category
  end

  # GET /categories/new
  def new
    @category = Category.new
    authorize @category
  end

  # GET /categories/1/edit
  def edit
    authorize @category
  end

  # POST /categories
  def create
    @category = Category.new(category_params)
    authorize @category
    if @category.save
      redirect_to @category, notice: 'Category was successfully created.'
    else
      render :new
    end
  end

  # PATCH/PUT /categories/1
  def update
    authorize @category
    if @category.update(category_params)
      redirect_to @category, notice: 'Category was successfully updated.'
    else
      render :edit
    end
  end

  # DELETE /categories/1
  def destroy
    authorize @category
    @category.destroy
    redirect_to categories_url, notice: 'Category was successfully destroyed.'
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_category
    @category = Category.find(params[:id])
  end

  # Only allow a trusted parameter "white list" through.
  def category_params
    params.require(:category).permit(:name)
  end
end

application_policy.rb

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def create?
    create?
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope.all
    end
  end
end

我的 ApplicationController 中包含 Pundit,rescue_from Pundit::NotAuthorizedError, with: :forbidden 也在那里设置。

授权本身有效,如果我使用管理员帐户登录,我可以访问 /categories/*。如果我已注销,我会收到以下信息:NoMethodError at /categories undefined methodadmin?'对于零:NilClass` 在写这个问题时,我认为我发现了问题——我猜 Pundit 会寻找一个 nil 的用户,因为我没有登录。解决这个问题的正确方法是什么?

最好的问候

【问题讨论】:

    标签: ruby-on-rails ruby pundit


    【解决方案1】:

    最常见的方法是将用户从非登录用户无法访问的页面重定向。只需在控制器中添加一个 before 操作:

    class CategoriesController < ApplicationController
      before_action :redirect_if_not_logged_in
    
      <...>
    
      private
    
      def redirect_if_not_logged_in
        redirect_to :home unless current_user
      end
    end
    

    (我在这里假设您有 current_user 方法,该方法返回用户实例或 nil。请将 :home 更改为您要重定向用户的任何位置)

    【讨论】:

    • 感谢您的快速回答。这正是我一直在寻找的。真的不知道为什么我没有提出重定向。
    【解决方案2】:

    有多种方法可以实现您想要的。

    1. 最明显(但有点脏)和直截了当的方法是在每个条件下添加对用户存在的检查:

      user && user.admin?

      它不会因nil 错误而失败,因为条件的第二部分不会被执行。但它看起来不太好,对吧?特别是如果您必须将其复制到 CategoryPolicy 中的所有方法中。

    2. 您可以做的是让 Pundit “认为”您通过了 User,方法是创建一个 GuestUser 类,该类以 false 响应 admin? 方法 (https://en.wikipedia.org/wiki/Null_object_pattern):

      在面向对象的计算机编程中,空对象是没有引用值或具有已定义中性(“空”)行为的对象。空对象设计模式描述了此类对象的用途及其行为(或缺乏)

      usernil 时使用它。在实践中,它看起来像这样:

      class ApplicationPolicy
        attr_reader :user, :record
      
        def initialize(user, record)
          @user = user || GuestUser.new
          @record = record
        end
      
        # ...
      end
      
      class GuestUser
        def admin?
          false
        end
      end
      

      这样您就不必更改任何其他代码,因为您传递的模型会响应策略所期望的方法 (admin?)。您可能希望在其他位置(不在策略文件中)定义此 GuestUser,具体取决于您是否希望应用的其他部分重用该行为。

    3. 您还可以继续使用 P. Boro 回答中的重定向方法。它在某种意义上不太灵活,但如果您除了重定向所有未登​​录的用户之外不需要任何其他东西,它完全可以正常工作。

    【讨论】:

      猜你喜欢
      • 2014-11-10
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-23
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多