5

我正在使用带有 rails 3 的 Devise 3.1.1,并且我的布局中有这个闪存处理代码:

<% flash.each do |name, msg| %>
    <%= content_tag :section, msg, :id => "flash_#{name}", :class => "flash" %>
<% end %>

我登录我的应用程序,flash 说:

"Signed in successfully."

然后退出,然后登录不正确,闪光灯说:

"Signed out successfully."
"Invalid email or password."

我想我明白为什么我会收到两条消息,当登录不正确时,没有重定向,只是一个渲染。

不知道如何解决它。

4

1 回答 1

25

我找到了原因。

当你深入到 Devise 的 SessionsController 源代码时,你会发现#create方法如下:

# POST /resource/sign_in
def create
  self.resource = warden.authenticate!(auth_options)
  set_flash_message(:notice, :signed_in) if is_navigational_format?
  sign_in(resource_name, resource)
  respond_with resource, :location => after_sign_in_path_for(resource)
end

在上面的代码中,Devise 在此处设置了成功登录的 Flash 消息。这就是您看到的消息"Signed in successfully."。它使用的方法set_flash_message只是flash[key]= "something". #destroy向您展示的方法也是如此"Signed out successfully"

请注意,在上面的代码中,没有设置错误消息的代码,例如“无效的密码或电子邮件”。那么你看到这条消息是怎么来的?它设置在Devise::FailureApp

def recall
  env["PATH_INFO"]  = attempted_path
  flash.now[:alert] = i18n_message(:invalid)
  self.response = recall_app(warden_options[:recall]).call(env)
end

注意这里,方法是flash.now,不是flash。不同之处在于flash.now将在当前请求中传递 Flash 消息,而不是下一个。

默认情况下,将值添加到闪存将使它们可用于下一个请求,但有时您可能希望在同一个请求中访问这些值。例如,如果创建操作未能保存资源并且您直接呈现新模板,则不会导致新请求,但您可能仍希望使用 flash 显示消息。为此,您可以像使用普通闪存一样使用 flash.now。http://guides.rubyonrails.org/action_controller_overview.html#the-flash

所以现在原因揭晓了。

  1. 你退出了。你打SessionsController#destroy。设计破坏了您的会话,将您带到/users/sign_in,再次为您的登录呈现'new模板。flash 对象包含成功注销的消息,您看到了它。

  2. 现在您尝试登录同一页面。这次你的表单提交命中#create。如果出错,Devise 不会将您重定向到任何地方,而是使用包含登录错误消息'new'的对象再次呈现相同的模板。flash.now

在第 2 步中,最后一个 flash 对象没有被删除,因为没有呈现新请求,而是flash.now添加了另一个新对象。所以你会看到两条消息。

解决方案

当然,可以覆盖 Devise 来改变这种行为,但这既麻烦又不必要

一个更方便和用户友好的解决方案是,不要在登录或退出后将用户登陆到登录页面

这很容易通过设置store_location和覆盖after_sign_in_path_for以及after_signed_out_path_for在您的应用程序控制器中。

def store_location
  disable_pattern = /\/users/
  session[:previous_url] = request.fullpath unless request.fullpath =~ disable_pattern
end

def after_sign_in_path_for(resource)
  session[:previous_url] || root_path
end

def after_sign_out_path_for(resource)
  after_sign_in_path_for(resource)
end

通过此设置,用户将在登录或退出后登陆他之前浏览过的页面,并且他们将不会再在问题中看到两条闪现消息。

原因是,当用户退出时,他将被重定向到上一页并看到退出消息。当他想登录时,他需要去登录页面,这是一个新的请求,然后之前注销的闪存将被删除。

于 2013-10-13T10:43:20.510 回答