为什么不引用Model层中的会话哈希呢



这个问题已经被反复提出,并用一行断言来回答,比如"因为这明显违反了MVC。"坦率地说,我只是不明白。事实上,在我看来,把会话放在控制器中只是ApplicationController通过机架调用面向网络层的工件,而不是MVC命令。让我解释一下我的问题。

从头开始进行身份验证时,我发现自己因为缺乏表达简单测试的能力而痛苦不已(测试框架也无法使用会话)。我的身份验证方案,就像我在rails中看到的几乎所有方案一样,希望使用会话哈希作为持久层来保留"当前用户"的用户模型的id。这感觉不是更像是一个模型而不是控制器工件吗?

每当你看到"典型"的会话控制器(这是Ryan Bates出色的屏幕截图)时,代码的气味就会很明显。急于把这个概念和休息结合起来,我们看到了不健康的语言,比如:

def create 
  user = User.find_by_email(params[:session][:email])
  if user && user.authenticate(params[:session][:password])
    session[:user_id] = user.id
    redirect_to root_url, notice: "Logged in!"
  else
    flash.now.alert = "Email or password is invalid"
    render "new"
  end
end

对我来说,这是一种代码的味道,一个明显过载的控制器,正在尖叫着进行重构!但我们做不到。为什么?哦,是的,因为将对会话的引用作为持久性律师放入模型中是违反MVC的。WTF?这不是在告诉你,我们似乎想称之为休息资源/会话吗?

要了解为什么这只是一个普通的古怪,请查看您的登录视图——手工编码的html,或者使用"_tags"API?如果我们有一个ActiveModel模型来执行这些代码,那么创建代码可能看起来像通常的脚手架,甚至可能简化为"responsd_with"一行。

def create 
  recognition = Recognition.new(params[:user])
  if recognition.save
    redirect_to root_url, :notice => "Thank you for signing up!"
  else
    render "new"
  end
end

然后,看看所有这些视图的手工编码html!如果Recognition是一个模型,通过会话持久化(或者其他方式,无论如何都不应由控制器层负责),那么您可以简单地使用表单生成器或simple_form来生成表单。当然,我们可以简单地将会话哈希传递给"new_login"类的Recognition方法,比如Recognition.on(session).new(params[:recognition]),但这似乎比实际情况更糟糕。也许这是固有的,因为我们稍后会想在应用层中使用current_user引用,也许Recognition.on(session).current_user类似于使用singleton模式的方式?

试着用严格的BDD构建你的身份验证包,然后诚实地告诉我你没有添加这部分内容?如果我们有一个识别模型,整个过程将简化为一组简单的单元测试,而不需要技巧。现在,我们有了集成测试的"唯一"用例、ActiveController模块的神奇入侵以及对logged_in_as谓词进行快速验收测试的技巧。

我认为ActiveModel的全部目的是促进这种重新思考和重构。并非所有模型都使用"the"数据库。为什么不坚持到"会话?"

我已经使用这个装置及其亲属太久了,以"不要惹宝石"为借口掩盖这些气味,我不必看它们。不再我想从现在开始我会拒绝狂热者。对不起,对我来说,会话是一个持久层,应该在MVC的Model层中进行操作。我认为,但不确定,它之所以存在于controllerland中,更多地与控制器是机架对象这一丑陋或优雅的事实有关,而不是任何理论上的MVC魔法。

那么,有没有比在控制器中设置逻辑更优雅的方法来访问会话层呢?

也许只有我一个人,但我不会闻到控制器中的代码气味。我想这取决于你认为控制器和模型应该做什么。

我认为人们有时会把"瘦控制者"的想法推向不健康的极端。是的,您希望模型中的所有内容都在模型上:但控制器的存在是为了根据应用程序状态更改模型,您应该允许它们实现设计目标。让每个控制器都像:

def create
  Creator.create(params) # Pass all params to the creator model, which will automatically detect what object you're trying to create, make it, and then render the appropriate view
end
def show
  Shower.show(params) # Find the model object and the view based on the params and render them together
end

无视关注点分离的想法,并为试图更新和维护代码的人带来噩梦般的问题。控制器应该调用模型对象甚至这些模型上的方法来创建和保持应用程序状态,并且模型应该与应用程序的状态无关。如果你把它们结合得太紧密,你的模型中就会出现视图和控制器代码,这时很难确定应用程序中的位置。

如果这是你想要的,并且你认为它符合你的目标,那么就去做吧——但你的代码将更难维护,其他人也很难理解。MVC之所以存在,是因为它是一种合理的分离关注点的方式,并使您的代码不那么让其他人感到惊讶。

话虽如此,你提出的会话模式实际上是个不错的主意。。。因此,它已经存在。)authlogic框架有一个会话模型:当使用authlogic登录时,您将使用params对象创建一个新的UserSession。UserSessions位于models文件夹中,其存在只是为了将身份验证的实质抽象到一个感知控制器的模型类中,所以如果这是您想要的,那么它已经为您完成了!查看authlogic github存储库中的文档和使用示例。

不过,我仍然会回避将任何类型的控制器状态传递到真正的ActiveRecord模型中。允许您的控制器操作模型,并将操作结果呈现为HTML——这就是它们的用途!

最新更新