ruby 如何避免未定义方法错误

lztngnrs  于 2023-02-03  发布在  Ruby
关注(0)|答案(3)|浏览(189)

我想检查当前对象的user-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.如果您在控制器的“private”部分定义了一个方法(def x),这意味着数据只在您的控制器中可用?
1.通过回调(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字段已填充,因此我不知道为什么会出现此错误。

qncylg1j

qncylg1j1#

根据您的错误消息,问题出在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]
mrwjdhj3

mrwjdhj32#

你要问的是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;解决方案是确保X1 M7 N1 X被声明

错误似乎是由以下原因引起的:

@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处理。

6ojccjat

6ojccjat3#

你可以使用respond_to?方法来检查一个对象在调用一个特定的方法之前是否能够响应它。

object.respond_to?(:method_name)

相关问题