设计和处理闪光灯 [英] Devise and handling the flash
问题描述
%flash.each do | name,msg | %GT;
<%= content_tag:section,msg,:id => flash _#{name},:class => flash%>
<%end%>
我登录我的应用程序,Flash说:
成功登录。
然后退出登录,然后登录错误,Flash说:
成功注销。
电子邮件或密码无效。
我想我明白为什么我收到两条消息,当登录不正确时,没有重定向一个渲染。
不知道如何修复它。
我想出了原因。
当您挖掘Devise的SessionsController来源时,您会发现 #create
方法如下:
#POST / resource / sign_in
def create
self.resource = warden。 (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设置了闪烁的消息,成功登录这里。这是您看到的消息,因为成功登录。
。它使用方法 set_flash_message
,它只是一个 flash [key] =something
的包装器。 #destroy
方法也是如此,它显示您成功注销
。
注意在上面的代码中,有没有代码设置错误信息,如密码或电子邮件无效。那么你看到这个消息呢?它设置在 Devise :: FailureApp
def recall
env [PATH_INFO] = attempts_path
flash.now [:alert] = i18n_message(:invalid)
self.response = recall_app(warden_options [:recall])。call(env)
end
请注意,方法是 flash.now
,而不是 flash
。区别是 flash.now
将在当前请求中发送Flash消息,而不是下一个。
默认情况下,将值添加到闪存将使它们可用于下一个请求,但有时您可能希望在同一请求中访问这些值。例如,如果创建操作无法保存资源,并且直接呈现新模板,则不会导致新请求,但您可能仍然希望使用闪存显示消息。要做到这一点,您可以使用flash.now以与使用普通闪光灯相同的方式使用。 http://guides.rubyonrails.org/action_controller_overview.html#the-flash
所以现在揭示了原因。
-
您已退出。你碰到
SessionsController#destroy
。 Devise破坏了你的会话,让你再次登录到/ users / sign_in
,渲染'new
模板。 Flash对象包含成功的签出消息,您看到它。 -
现在,您尝试在同一页面上登录。这次您的表单提交命中
#create
。如果错误,Devise不会重定向到任何地方,但再次使用flash.now
再次渲染相同的'new'
对象,其中包含登录错误消息。
在步骤2中,最后一个flash对象不会被删除,因为没有新的请求渲染,但添加了另一个新的 flash.now
对象。所以你看到两个消息。
解决方案
当然可以覆盖Devise来改变这种行为,但是这很麻烦,不必要。
一个更方便和用户友好的解决方案是,不要登陆用户登录页面无论是登录还是退出。
通过设置 store_location
并覆盖 after_sign_in_path_for
和 after_signed_out_path_for
在您的应用程序控制器中。
def store_location
disable_pattern = / \ / users /
会话[:previous_url] =请求。 fullpath,除非request.fullpath =〜disable_pattern
end
def after_sign_in_path_for(资源)
会话[:previous_url] || root_path
end
def after_sign_out_path_for(资源)
after_sign_in_path_for(资源)
结束
通过此设置,用户将在登录或注销后登陆他之前浏览过的页面,并且不会在该问题中再次看到两个Flash消息。
原因是当用户退出时,他将重定向到上一页,并看到已签出的消息。当他想要登录时,他需要去登录页面,这是一个新的请求,那么以前的注销闪存将被删除。
I am using Devise 3.1.1 with rails 3 and I have this flash handling code in my layout:
<% flash.each do |name, msg| %>
<%= content_tag :section, msg, :id => "flash_#{name}", :class => "flash" %>
<% end %>
I sign into my app, flash says:
"Signed in successfully."
then sign out, then sign in incorrectly and flash says:
"Signed out successfully."
"Invalid email or password."
I think I understand why I am getting two messages, when signing in incorrectly there is no redirect, just a render.
Not sure how to fix it though.
I figured out the reason.
When you dig into Devise's source of SessionsController, you'll find #create
method as follows:
# 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
In above code, Devise sets flash message for success signed in here. That's what the message you saw as "Signed in successfully."
. It uses the method set_flash_message
which is just a wrapper of flash[key]= "something"
. The same is true for #destroy
method which show you "Signed out successfully"
.
Note in above code, there is no code to set error message such as "Invalid password or email". So how comes this message you saw? It is set in 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
Note here, the method is flash.now
, not flash
. The difference is flash.now
will deliver flash message in current request, not next.
By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the create action fails to save a resource and you render the new template directly, that's not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use flash.now in the same way you use the normal flash. http://guides.rubyonrails.org/action_controller_overview.html#the-flash
So the reason is revealed now.
You signed out. You hit
SessionsController#destroy
. Devise destroyed your session, brings you to/users/sign_in
, render'new
template for your sign in again. The flash object contains the successful signed out message and you saw it.Now you tried to sign in in same page. This time your form submit hit
#create
. If error, Devise will not redirect you to anywhere but render the same'new'
template again with theflash.now
object which contains sign in error message.
In step 2, you last flash object is not removed because no new request rendered, but another new flash.now
object is added. So you see two message.
Solution
Of course it's possible to override Devise to change this behaviour, but that's troublesome and unnecessary.
A more convenient and user-friendly solution is, do not land the user on sign in page after either signed in or signed out.
This is easily by setting store_location
and override after_sign_in_path_for
and after_signed_out_path_for
in your application controller.
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
By this setting the user will land on his previously browsed page either after signed in or signed out, and they will not see two flash messages in the question again.
The reason is, when the user signed out, he will be redirect to previous page and see the signed out message. When he want to sign in, he need to go to sign in page which is a new request, then the previous signed out flash will be removed.
这篇关于设计和处理闪光灯的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!