class User < ActiveRecord::Base
def self.current
Thread.current[:user]
end
def self.current=(user)
Thread.current[:user] = user
end
end
就是这种在 Model 里面拿 current_user 的方法。 最近似乎遇到了用户乱窜的情况。。获取错了似乎。
好不好用和项目有关,
小项目,无所谓,
大项目,不好。
我们之前就是用 User.current
方式
导致,model 层代码,严重依赖 User.current
,导致系统极度混乱。
后来,改成了Controller current_user
模式,世界从此清净了!
#7 楼 @ywencn 对,每次都从 Controller 传递过去。 举个例子
class BooksController < ApplicationController
def index
@books = current_user.books
end
def create_1
@book = current_user.books.create
end
def create_2
@book = Book.create
@book.user = current_user
end
end
其实 从 MVC 框架逻辑上分析
current_user
属于 会话层,属于一个临时的状态,而非持久性的状态。
所以 它更应该 属于 Controller 层
不应该属于 Model 层
User.current 这种用法,从 redmine 里来的吧 XD
传送门 https://github.com/redmine/redmine/blob/2.3.2/app/models/user.rb#L590
def self.current=(user)
Thread.current[:current_user] = user
end
def self.current
Thread.current[:current_user] ||= User.anonymous
end
这个问题曾经在 rails bestpractices 上有激烈的争论: http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
用户乱串 我们也遇到过,因为后面的用户复用了之前的线程变量。
所以应该在每次请求结束后清除线程变量,可以放到 application_controller 中
after_filter :clear_thread_variable
def clear_thread_variable
Thread.current[:user] = nil
end
在 em-synchrony, celluloid, 或者线程池部署都行不通
另外 Thread local 在 Ruby 2.1 之前都是 Fiber local :
Fiber.new { Thread.current[:a] = 3 }.resume
Thread.current[:a] #=> nil
这种方式在 java 的项目中常用,因为 java 都是线程模型的,但在 ruby 上,如果是 unicorn 中中并发模型,还是可以的,记得设置完后清除掉 Thread 上的变量否则下次请求或者异常情况下被设置错误的变量。但在 Fiber 并发下,可能就不行了。
最近也同样遇到了这个问题,翻了几篇文章,觉得这篇讨论的挺详细的。 http://rails-bestpractices.com/posts/47-fetch-current-user-in-models 这里面有大段的关于 Thread 取 current_user 是否是好设计的讨论。@ywencn @hooopo 说得两种方式,其实比较像,第二种用 attr_accessor 比较巧妙的缓存了 session[:user_id](可以是 author, operator 等等随意)
flyerhzm 的其中之一的解释是 Thread.current is used to store and fetch the thread local variables. That means two threads have the same variables but these variables are referring to the different memory locations. If you see the rails 3.0.rc source codes, you will find rails also use Thread.current for time_zone, active_record_sql_runtime and etc.
我觉得 ywencn 说得是协作者修改创作者的操作记录的问题。所以 hp 说得第一种方案就不多说了。我的观点: 虽然 hp 的方式看起来更安全一点(也很巧妙,这是毋庸置疑的!)。我更喜欢 Thread 这种方式,从设计的角度来说,假如我有三处需要这个 session[:user_id](如 create, update, destroy),在 controller 每次都需要有一个{user_id: current_user.id},这实际上违反了 rails 的一些约定,另更多的重复会有更多的 bug,controller(有可能多个)跟 model 层耦合的更紧,扩展性也会差一些。我觉得如果真觉得有不对,可以多写一些 log 追踪一下。