【问题标题】:How to avoid undefined method error如何避免未定义的方法错误
【发布时间】:2016-03-12 14:23:42
【问题描述】:

我想检查当前对象的用户id是否与当前用户的id相同,所以我可以只允许登录用户进行一些操作。我正在使用 Devise gem 来帮助我进行身份验证。

也就是说,我想问一个范围更广的问题。我已经建立了关联,至少我是这么认为的,但是当我在浏览器中打开相应页面时出现错误:

undefined method 'user' for nil:NilClass

我知道当数据库中的特定对象未实例化或没有条目时,通常会发生此错误,但我使用控制台和 PostgreSQL GUI 工具来检查数据是否存在。

这是截图https://www.evernote.com/shard/s233/sh/305c5194-87e0-4019-9eba-9a7f5d7a2839/7c89b4842cc6efc1/res/b7879832-7829-4fe3-b81a-386b6f81cc11/skitch.png?resizeSmall&width=832

首先澄清一下我的理解是对的,下面是一些事情的作用:

  1. 如果您在控制器的“私有”部分中定义方法 (def x),这意味着数据仅在您的控制器中可用?
  2. 通过回调 (before_action),您可以使用私有方法的数据填充应用的 REST 方法,它可能想要使用?

现在我有一个图像模型:

class Image < ActiveRecord::Base
  mount_uploader :image, ImageUploader
  belongs_to :user
  belongs_to :game, inverse_of: :images
end

用户模型如下所示:

class User < ActiveRecord::Base
  ...
  has_many :images
  has_many :games
  validates :first_name, :last_name, presence: true
end

在我使用的对应图像控制器中:

class ImagesController < ApplicationController
  before_action :set_image, only: [:show, :edit, :update, :destroy]
  before_action :set_game
  before_action :authenticate_user!
  before_action :check_user
  ...
  private

  def set_image
    @image = Image.find(params[:id])
  end

  def set_game
    @game = Game.all
  end

  def check_user
    unless (@image.user == current_user) || (current_user.admin?)
      redirect_to root_url, alert: "Sorry but you are not allowed to visit this page."
    end
  end

  def image_params
    params.require(:image).permit(:title, :alt, :desc, :image, :category)
  end
end

check_user 方法中使用@image.user,我尝试获取用户的ID。如果我只使用 current_user.admin? 它可以工作,但显然不是预期的。

正如您在上面的屏幕截图中所见,user_id 字段已填充,所以我不知道为什么会出现此错误。也许我忘记了什么?

【问题讨论】:

    标签: ruby-on-rails ruby postgresql devise


    【解决方案1】:

    你要问的是一个叫做authorization的东西。

    • 身份验证 - 用户存在吗?
    • 授权 - 用户有权限吗?

    Devise 提供身份验证,而授权 没有 Rails 的“标准”流程。

    您要问的是在基于 Rails 的应用程序中授权的基线要求。解决此问题的方法是使用授权 gem 之一,即 CanCanCanPundit 以确保用户可以更改所需的对象。

    我个人设置授权如下:

    #Gemfile
    gem 'cancancan'
    
    #app/models/ability.rb
    class Ability
      include CanCan::Ability
    
      def initialize(user)
        user ||= User.new # guest user (not logged in)
        can :read, Image, user_id: user.id
      end
    end
    

    这将允许您简单地调用can? :read, @image 来验证用户的授权。


    修复

    您遇到的真正问题是您试图在不存在的变量上调用.user

    for nil:NilClass
    

    当您看到上述错误时,这意味着您正在对未声明的变量调用方法。

    与其他编程语言不同,Ruby 并没有将变量视为未声明,而是将其视为nil - 让许多开发人员感到困惑。简而言之,错误意味着您试图在不存在该方法的变量上调用.user;解决方案是确保声明@image

    -

    错误似乎是由这个引起的:

    @image.user #-> @image does not exist
    

    因此,您必须检查 为什么 @image 尚未声明。

    我会冒险猜测错误是由您的routes 引起的。您需要确保正确调用 images 控制器:

    #config/routes.rb
    resources :images
    
    #app/controllers/images_controller.rb
    class ImagesController < ApplicationController
       def show
          @image = Image.find params[:id]
          authorize! :read, @image
       end
    end
    

    这应该只有拥有图像的用户可以查看它。您不必担心身份验证,因为这将由 Devise 处理。

    【讨论】:

    • @Rick:感谢您的洞察力。这很有帮助。我的个人授权逻辑阻碍了整个事情,我不得不排除一些方法,比如:show和:index。我在那里发布的错误消息与我没有登录的情况有关。当我重新登录时它就消失了。如果 Rails 有朝一日可以提供更多关于为什么一个对象可能是“nil”的信息,那将非常有帮助,另一方面,比许多开发人员不会动脑筋思考。最后我得到了它的工作。这才是最重要的,我也学到了一些东西。非常感谢。 :)
    • 没问题!显示变量是否为nil 不是Rails 的责任——这是Ruby 的问题。这都是因为 Ruby 是面向对象的
    【解决方案2】:

    根据您的错误消息,问题出在check_user 方法中的@image.user 上。这里,@imagenil。你应该检查那里是否有@image.nil?

    大概改成:

    @image = Image.find(params[:id])
    unless !@image.nil? && ((@image.user == current_user) || (current_user.admin?))
    

    顺便说一句,您应该只检查:show, :edit, :update, :destroy 中的用户,例如:

    before_action :check_user, only: [:show, :edit, :update, :destroy]
    

    【讨论】:

    • 是的,问题出在检查用户内部。但我有一个有效的 user_id 图像字段。难道现在没有过滤器的身份验证用户与检查用户冲突吗?后来我在回调中添加了过滤器,没有使用 :show 过滤器,因为我希望人们能够看到单个图像。在下一步中,当我让此身份验证工作时,我计划执行不同的路由并将编辑、更新和销毁功能打包到后端之类的东西中。谢谢
    • 是的,但问题是 @imagenil 而不是 @image.usernil
    • 这很奇怪,因为@image 应该在我们说话的那一刻提供至少 4 张图片。数据库中至少有 4 张图像。如果我注释掉除非部分并正常打开页面,:index 例如会显示带有 4 张图像的列表。 :(
    • 您在数据库中有图像,但您需要在您的方法check_user 中定义@image 是什么。否则Rails 不明白@image 是什么意思。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-28
    • 1970-01-01
    • 2010-11-14
    相关资源
    最近更新 更多