如何在AJAX中优雅地处理设计401的状态?

时间:2012-04-12 13:43:10

标签: ruby-on-rails ajax authentication devise

使用设计,只需使用before_filter :authenticate_user!来限制对经过身份验证的用户的访问。

未经身份验证的用户尝试访问受限制的页面时,设计会自动导致重定向到登录页面。

因此,尝试打开http://localhost:3000/users/edit会导致重定向到http://localhost:3000/users/sign_in

现在,如果我将链接http://localhost:3000/users/edit定义为:remote => true,设计只会通过JS发出 401状态代码

我如何优雅地应对这种情况并在覆盖重定向中显示登录对话框,因为非远程变体会这样做?

设计是否为我需要激活的情况提供默认策略?

7 个答案:

答案 0 :(得分:15)

$(document).ajaxError(function (e, xhr, settings) {
        if (xhr.status == 401) {
           $('.selector').html(xhr.responseText);
        }
    });

答案 1 :(得分:12)

这是我现在选择的解决方案(在CoffeeScript语法中):

$ ->
  $("a").bind "ajax:error", (event, jqXHR, ajaxSettings, thrownError) ->
    if jqXHR.status == 401 # thrownError is 'Unauthorized'
      window.location.replace('/users/sign_in')

然而,这(仅此而已)只是忘记了用户最初想要访问的页面,这限制了可用性。

需要额外的(控制器)逻辑才能实现更优雅的处理。

更新:正确重定向

在该函数中,this包含用户打算转到的初始URL。

通过调用window.location.replace(this)而非显式重定向到登录页面),应用会尝试将用户重定向到最初预期的目的地。

虽然仍然不可能(未经授权),但现在这将是一个GET调用(而不是JS / AJAX)。因此,设计可以启动并将用户重定向到登录页面。

从那时起,Devise像往常一样运作,在成功登录后将用户转发到最初的目标网址。

答案 2 :(得分:2)

location.reload()结合的事件绑定版本:

$(function($) {
  $("#new-user")
    .bind("ajax:error", function(event, xhr, status, error) {
      if (xhr.status == 401) {  // probable Devise timeout
        alert(xhr.responseText);
        location.reload();      // reload whole page so Devise will redirect to signin
      }
    });
});

使用Devise 3.1.1进行测试,这确实正确设置了session["user_return_to"],因此用户在再次登录后返回页面()。

我添加alert作为解决此处讨论的不良信息问题的简单方法:Session Timeout Message in RoR using Devise

答案 3 :(得分:2)

这是我在CoffeScript中的copy-past-hapy(tm)解决方案。它将所有401重定向到登录页面。

<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %>

$(document).ajaxError (_, xhr)->
  window.location = '<%= new_user_session_path %>' if xhr.status == 401

并在Javascript中:

<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %>

$(document).ajaxError( function(event, xhr){
  if (xhr.status == 401) {
    window.location = '<%= new_user_session_path %>'
  }
});

答案 4 :(得分:1)

如果您执行“:remote =&gt; true”,则可以在“ajax:error”事件绑定上使用.live。

$('#member_invite, #new_user')
        .live("ajax:success", function(evt, data, status, xhr){
          $.colorbox.close();
        })
        .live("ajax:error", function(evt, data, status, xhr){
          alert("got an error");
        });

其中“#new_user”将是表单ID值。

请注意,如果您已经有覆盖或对话框,那么更优雅的方式就是插入消息,而不是alert():

$('.messages').html('Invalid email or password');

并在您的登录表单中执行

<div class="messages"></div>

或者你甚至可以直接替换表格的标题,无论你需要什么。

答案 5 :(得分:1)

从Rails 5.1和新的rails-ujs开始,所有自定义事件仅返回一个参数:event。在此参数中,还有一个附加属性detail,其中包含一组附加参数。参数datastatusxhr已捆绑到event.detail中。因此,使用ajax:error事件处理ajax中的401错误变为:

document.body.addEventListener('ajax:error', function(event) {
  var detail = event.detail;
  var response = detail[0], status = detail[1], xhr = detail[2];
  if (xhr.status == 401) {
    // handle error
  }
})

答案 6 :(得分:0)

我很高兴看到有一种优雅的方式来做到这一点!

在那之前,我就是这样处理的。

在edit.js.erb视图文件中,您可以输入以下代码:

<% case response.status
  when 200
%>
  //do what you need to do
<% when 401 %>
  //handle the 401 case, for example by redirecting to root or something
  window.location.href('/');
<% else %>
  //catch all
  alert('We\'ve had a problem, please close this, refresh the page and try again');
<% end %>

如果是401,它将查看响应的状态代码并重定向到登录页面。

我想知道是否有办法直接在控制器级别处理这个问题。