在Rails 4中优雅地处理InvalidAuthenticityToken异常

Bee*_*Bee 15 session ruby-on-rails ruby-on-rails-4

我刚刚将一个应用程序从Rails 3升级到Rails 4,我看到一堆InvalidAuthenticityToken异常弹出.挖掘它看起来我们的用户在我们的网站上打开多个长期标签是相当普遍的.所以我认为发生的事情是这样的:用户Alice有三个标签打开,她的会话到期.她重新登录其中一个选项卡,这会更新存储在其会话中的真实性令牌.然后她返回到其他一个打开的选项卡并尝试提交数据,但她从我们引发的InvalidAuthenticityToken错误中得到500错误.

为Alice做一些错误处理显然很好,所以她没有得到500错误.我想知道这种情况的最佳做法.从过期的标签处理Alice的提交有什么好方法?我不想让当前会话到期,因为从用户的角度来看这会非常烦人("我登录,你笨蛋!").理想情况下,我只是希望用户重新加载页面,这将导致表单中存在正确的真实性令牌.或者我应该做一些不同的事情,以便打开的长期标签注意到会话已过期并强制重新加载?从用户的角度来看,这可能是次优的,因为他们喜欢准备好该页面并且可以轻松访问以保持引用,这就是为什么他们首先在选项卡中将其打开.

Jag*_*ngh 14

该问题的解决方案可分为两个阶段.第1阶段解决了ActionController::InvalidAuthenticityToken错误问题,第2阶段处理长标签闲置等问题.

第1阶段(第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
Run Code Online (Sandbox Code Playgroud)

2)您需要为所有表单字段包含默认值.它将是该字段的名称.

...
<% f.text_field, name: 'email', placeholder: 'Email', value: params[:email] %>
...
Run Code Online (Sandbox Code Playgroud)

这样,每当Alice提交错误的表单时,authenticity_token她将被重定向回她的表单,并提供她提交的原始值,并且将显示一条闪回消息,请您重试您的请求.

第1阶段(第2次变更)

另一种方法是将Alice重定向回她提交的表单,而不提供任何预先填充的值.

这种方法可通过以下方式实现:

1) ApplicationController - 添加此功能:

def handle_unverified_request
    flash[:error] = 'Kindly retry.'
    redirect_to :back
end
Run Code Online (Sandbox Code Playgroud)

阶段2

为了解决期待已久的标签问题,您可以借助SSE的帮助.Rails 4 ActionController::Live用于处理SSE.

1)将其添加到任何控制器:

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
Run Code Online (Sandbox Code Playgroud)

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 %>
Run Code Online (Sandbox Code Playgroud)

注意:并非所有浏览器都支持使用EventSource.请查看浏览器兼容性部分.

来源: rails 4使用新的参数MDN 重定向:使用服务器发送的事件


Dur*_*rro 10

在我的rails 4.1.1应用程序中我遇到了同样的问题.我通过将此代码添加到ApplicationController来解决它.我在这里找到了解决方案.

rescue_from ActionController::InvalidAuthenticityToken, with: :redirect_to_referer_or_path

def redirect_to_referer_or_path
  flash[:notice] = "Please try again."
  redirect_to request.referer
end
Run Code Online (Sandbox Code Playgroud)

这样,任何从ApplicationController继承的控制器都会处理错误,并通过flash消息重定向提交表单的页面,以便向用户提供错误指示.请注意,这使用Ruby 1.9中引入的哈希语法.对于旧版本的Ruby,您需要使用:with => :redirect_to_referer_or_path


abh*_*7in 8

达雷尔的回答完全没问题,我只是提供另一种方式来写同样的事情。这个需要进去ApplicationController

rescue_from ActionController::InvalidAuthenticityToken do |_exception|
  flash[:alert] = 'Please try again.'
  redirect_back fallback_location: root_path
end
Run Code Online (Sandbox Code Playgroud)

  • 我只想补充一点,`redirect_back` [仅在 Rails 5 及更高版本上可用。](https://github.com/rails/rails/pull/22506) (2认同)