Ruby on rails 3 将需要知道当前用户的业务逻辑放在何处?(轨道)

Ruby on rails 3 将需要知道当前用户的业务逻辑放在何处?(轨道),ruby-on-rails-3,Ruby On Rails 3,我有一个模型(比如说Car),其中一个方法需要访问当前用户,以确定是否允许用户执行该方法所做的事情 例如,一个方法可能需要执行以下操作: 检查当前用户是否拥有此对象 检查对象状态==1(活动) 检查相关对象是否存在,其X字段是否为空 我需要这个业务逻辑在模型中,而不是在控制器中,这样它才是我的业务逻辑所在的地方。该方法可能从控制器以外的位置调用 class ApplicationController < ActionController::Base protect_from_forgery

我有一个模型(比如说
Car
),其中一个方法需要访问当前用户,以确定是否允许用户执行该方法所做的事情

例如,一个方法可能需要执行以下操作:

  • 检查当前用户是否拥有此对象
  • 检查对象状态==1(活动)
  • 检查相关对象是否存在,其X字段是否为空
  • 我需要这个业务逻辑在模型中,而不是在控制器中,这样它才是我的业务逻辑所在的地方。该方法可能从控制器以外的位置调用

    class ApplicationController < ActionController::Base
    protect_from_forgery
    
      protected 
        # Returns the currently logged in user or nil if there isn't one
        def current_user
          return unless session[:user_id]
          @current_user ||= User.find_by_id(session[:user_id]) 
        end
    
        # Make current_user available in templates as a helper
        helper_method :current_user
    
        # Filter method to enforce a login requirement
        # Apply as a before_filter on any controller you want to protect
        def authenticate
          logged_in? ? true : access_denied
        end
    
        # Predicate method to test for a logged in user    
        def logged_in?
          current_user.is_a? User
        end
    
        # Make logged_in? available in templates as a helper
        helper_method :logged_in?
    
        def access_denied
          redirect_to login_path, :notice => "Please log in to continue" and return false
        end
    end
    
    我知道有些宝石像cancan、声明性授权等,但它们似乎对我需要做的事情来说太过分了。而且,访问模型中的当前用户也不被认为是“正确的方式”


    那么,如何在模型中进行检查,但仍然感觉“干净”?

    在控制器中处理身份验证

    class ApplicationController < ActionController::Base
    protect_from_forgery
    
      protected 
        # Returns the currently logged in user or nil if there isn't one
        def current_user
          return unless session[:user_id]
          @current_user ||= User.find_by_id(session[:user_id]) 
        end
    
        # Make current_user available in templates as a helper
        helper_method :current_user
    
        # Filter method to enforce a login requirement
        # Apply as a before_filter on any controller you want to protect
        def authenticate
          logged_in? ? true : access_denied
        end
    
        # Predicate method to test for a logged in user    
        def logged_in?
          current_user.is_a? User
        end
    
        # Make logged_in? available in templates as a helper
        helper_method :logged_in?
    
        def access_denied
          redirect_to login_path, :notice => "Please log in to continue" and return false
        end
    end
    
    示例:将身份验证逻辑放入父应用程序控制器

    class ApplicationController < ActionController::Base
    protect_from_forgery
    
      protected 
        # Returns the currently logged in user or nil if there isn't one
        def current_user
          return unless session[:user_id]
          @current_user ||= User.find_by_id(session[:user_id]) 
        end
    
        # Make current_user available in templates as a helper
        helper_method :current_user
    
        # Filter method to enforce a login requirement
        # Apply as a before_filter on any controller you want to protect
        def authenticate
          logged_in? ? true : access_denied
        end
    
        # Predicate method to test for a logged in user    
        def logged_in?
          current_user.is_a? User
        end
    
        # Make logged_in? available in templates as a helper
        helper_method :logged_in?
    
        def access_denied
          redirect_to login_path, :notice => "Please log in to continue" and return false
        end
    end
    
    class ApplicationController“请登录以继续”,并返回false
    结束
    结束
    
    既然当前用户是登录用户的访问者,并且您可以在任何控制器中访问它,那么您可以在对模型执行任何操作之前在适当的控制器中执行授权逻辑


    不过,你是对的。模型不关心授权或谁在访问它们。

    我经历过一种情况,即“当前用户”需要与模型紧密连接,但我在控制器中处理了这一切,并且工作得很好。以下是一些例子:

    我的模特是“照片”。照片归用户所有,人们如何与照片互动显然与他们是否拥有照片密切相关

    在show action中,我需要加载用户对照片的现有评分(以便他们可以编辑照片),或者允许他们创建一张新照片:

    def show
      @photo = Photo.find(params[:id])
      if user_signed_in?      
        if @rating = current_user.ratings.find_by_photo_id(params[:id])
          @rating
          @current_user_rating = @rating.value
        else
          @rating = current_user.ratings.new
          @current_user_rating = "n/a"
        end
      end
    end
    
    当人们创建照片时,我希望它们自动分配给当前用户

    def new
      @photo = Photo.new
    end
    
    def create
      @photo = current_user.photos.create(params[:photo])
      if @photo.save
        redirect_to user_path(current_user), :notice => t('photo.notice.created')
      else
        render 'new'
      end
    end
    
    只有照片的所有者才能更改它们:

    def edit
      @photo = Photo.find(params[:id])
      if @photo.user == current_user
        render 'edit'
      else
        redirect_to user_path(current_user), :alert => t('application.error.unauthorized')
      end
    end
    
    def update
      @photo = current_user.photos.find_by_id(params[:id])
      @photo.update_attributes(params[:photo])
      if @photo.save
        redirect_to user_path(current_user), :notice => t('photo.notice.updated')
      else
        render 'edit'
      end
    end
    
    这种方法基于“当前用户”对象绑定到会话的约束,只有控制器知道。因此,简而言之,我还没有找到一个好方法来将“当前用户”集成到模型中,但是我已经找到(我认为)非常干净的方法来将模型和控制器连接在一起,这样控制器就可以提供这一点

    class ApplicationController < ActionController::Base
    protect_from_forgery
    
      protected 
        # Returns the currently logged in user or nil if there isn't one
        def current_user
          return unless session[:user_id]
          @current_user ||= User.find_by_id(session[:user_id]) 
        end
    
        # Make current_user available in templates as a helper
        helper_method :current_user
    
        # Filter method to enforce a login requirement
        # Apply as a before_filter on any controller you want to protect
        def authenticate
          logged_in? ? true : access_denied
        end
    
        # Predicate method to test for a logged in user    
        def logged_in?
          current_user.is_a? User
        end
    
        # Make logged_in? available in templates as a helper
        helper_method :logged_in?
    
        def access_denied
          redirect_to login_path, :notice => "Please log in to continue" and return false
        end
    end
    
    对于大多数问题,如果控制器开始变得混乱,一个相当简单的解决方案是获取一块逻辑并在模型中定义为一个方法,但需要一个参数=一个用户对象。然后,您可以从控制器将“current_user”馈送到该方法,模型将处理其余部分


    祝你好运!另外,如果有其他人对此有更好的想法,我很乐意听到

    这不是我所需要的-我的模型的业务逻辑需要在模型中,对于该业务逻辑,我需要访问当前的用户。你能给我一个你在模型中尝试做什么的例子吗?通常,模型不应该知道当前请求,因为这项工作是在控制器中完成的。但有时候你真的忍不住了。谢谢你抽出时间。。我已经用我需要做什么样的业务逻辑更新了这个问题。谢谢你的回答,谢谢。您的代码可能会工作,但将其放在控制器中似乎有点错误(仅限于我的意见)。我正在考虑将所有业务逻辑放在一个模块/类中,然后将其与需要的模型混合。这样,我知道所有的逻辑都在一个地方(很好),而且我不会让模型或控制器太混乱。你对此有什么想法?没有理由不把你所有的逻辑放在一个地方。只要您对结果感到满意,代码就会按预期工作,并且不会让您怀疑“特定的代码位又在哪里?”,那么您就处于良好状态。然而。。。(由于角色限制,请参阅下一篇评论)我使用Rails的次数越多,我就越能适应MVC模式中存在的web应用程序的“流”。对我来说,我希望大多数逻辑与数据库中的数据相关,因此它属于模型。处理会话和HTTP请求以从模型中获取内容的逻辑在控制器中感觉正确,而一点表示逻辑在视图中是有意义的。诀窍是,在这些边缘情况下,“当前用户”之类的东西看起来像数据,但实际上不是,它完全与HTTP请求相关。(下一条评论)所以,至少对我来说,一旦我熟悉某段代码是在操纵数据、处理http请求还是向用户提供信息;然后,将其放置在模型、控制器或视图(分别)之外的任何位置都会让人感觉不对劲。最后一个问题是,什么能让你的程序始终保持最干爽?将每种逻辑放在一个模块中可以做到这一点,但另一方面,这可能意味着您需要为一种情况创建四个视图文件,而不是一个视图+一点条件逻辑。所以,用两种方法测试,然后做任何事情