我刚刚将一个应用程序从Rails 3升级到Rails 4,并且看到一堆InvalidAuthenticityToken异常 pop 。挖掘它看起来很常见,对于我们的用户来说,在我们的网站上打开多个长期存在的标签页是很普遍的。因此,我认为正在发生的事情是这样的:用户Alice打开了三个选项卡,并且她的 session 已过期。她重新登录选项卡之一,该选项卡更新了存储在 session 中的真实性 token 。然后,她返回到其他打开的选项卡之一并尝试提交数据,但是她从我们提出的InvalidAuthenticityToken错误中得到了500错误。
对Alice进行一些错误处理显然很不错,这样她就不会收到500错误。我想知道这种情况下的最佳做法。从过期标签处理Alice提交的好方法是什么?我不想使当前 session 到期,因为从用户的 Angular 来看,这将非常烦人(“我刚刚登录,您真可恶!”)。理想情况下,我只希望用户重新加载页面,这将导致表单中存在正确的真实性 token 。还是我应该做些不同的事情,以使打开的长寿命选项卡会通知 session 已过期并强制重新加载?从用户的 Angular 来看,这可能不是最佳选择,因为他们喜欢准备好该页面并且可以轻松访问该页面以保持引用,这就是为什么他们首先在选项卡中将其保持打开状态的原因。
最佳答案
该问题的解决方案可以分为两个阶段。第1阶段解决了ActionController::InvalidAuthenticityToken
错误的问题,第2阶段处理了空闲时等待较长标签的问题。
阶段1(第一个变化)
一种解决方法是将用户重定向到错误发生之前的位置。对于前。如果Alice打开了3个选项卡,则第一个选项卡到期,并且Alice因为正在浏览而在其中再次登录。但是当她移动到具有URL'http://example.com/ex'的选项卡3并提交表单时。现在,不再显示错误,我们可以将她重定向回“http://example.com/ex”,其提交的表单值已经预先填充在表单中,以方便使用。
可以通过以下方法实现:
1) ApplicationController-添加此功能:
def handle_unverified_request
flash[:error] = 'Kindly retry.' # show this error in your layout
referrer_url = URI.parse(request.referrer) rescue URI.parse(some_default_url)
# need to have a default in case referrer is not given
# append the query string to the referrer url
referrer_url.query = Rack::Utils.parse_nested_query('').
merge(params[params.keys[2]]). # this may be different for you
to_query
# redirect to the referrer url with the modified query string
redirect_to referrer_url.to_s
end
2)您需要为所有表单字段都包含默认值。这将是该字段的名称。
...
<% f.text_field, name: 'email', placeholder: 'Email', value: params[:email] %>
...
这样,每当爱丽丝提交带有错误
authenticity_token
的表单时,她都会被重定向回具有提交的原始值的表单,并且将显示一条简短消息,请您重试您的请求。第1阶段(第二个版本)
另一种解决方法是将Alice重定向回她提交的表单,而无需任何预先填写的值。
此方法可以通过以下方法实现:
1) ApplicationController-添加此功能:
def handle_unverified_request
flash[:error] = 'Kindly retry.'
redirect_to :back
end
阶段2
要解决期待已久的标签问题,您可以借助SSE。 Rails 4具有用于处理SSE的
ActionController::Live
。1)将此添加到任何 Controller :
include ActionController::Live
...
def sse
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream, retry: 2000, event: 'refresh') # change the time interval to your suiting
if user_signed_in? # check if user is signed-in or not
sse.write('none')
else
sse.write('refresh')
end
ensure
sse.close
end
2)在路由文件中为上述功能提供
GET
路由。让我们将此路由称为“/ sse”3)将此添加到您的布局中:
<% if user_signed_in? %> # check if user is signed-in or not
<script>
var evtSource = new EventSource("/sse");
evtSource.addEventListener('refresh', function(e){
if(e.data == 'refresh'){
window.location = window.location.href;
}
});
</script>
<% end %>
资源:
rails 4 redirect back with new params和MDN: Using server-sent events
关于ruby-on-rails - 在Rails 4中优雅地处理InvalidAuthenticityToken异常,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36509300/