2

我在前端使用 angularjs,在后端使用 rails + devise 进行身份验证。

在前端,我添加了一个 responseInterceptor 来重定向到/#/sign_in来自任何 xhr 请求的任何 401 响应的页面,并使用 toastr 显示咆哮式弹出消息。

App.config(['$httpProvider', function ($httpProvider) {
  $httpProvider.responseInterceptors.push('securityInterceptor');
}]);

App.factory('securityInterceptor', ['$injector', '$location', '$cookieStore', function ($injector,$location,$cookieStore) {

  return function(promise) {
    var $http = $injector.get('$http');
      return promise.then(null, function(response){
        if (response.status === 401) {
          $cookieStore.remove('_angular_devise_user');
          toastr.warning('You are logged out');
          $location.path('/#/sign_in');
        }
      });
    };
});

我的问题是,当我在控制器初始化期间单击加载多个 xhr 请求的页面时,例如:

var products = Product.query();
var categories = Category.query();
var variations = Variation.query();

这些是各种导航组件所必需的,并且它们都并行触发,从而导致多个重复的咆哮式消息。

有没有办法在第一个 401 上进行角度退出并从拦截器内停止执行控制器的其余部分?在传统的 Rails 应用程序中,会有一个“before_filter”停止常规执行,阻止页面和查询加载......以角度执行此操作的最佳方法是什么?

4

1 回答 1

0

我也一直在为自己的应用考虑这个问题。我的想法的草图(不是真正的实现,所以要小心):

userData服务跟踪用户是否登录+其他信息(例如用户名、真实用户名等):

App.service("userData", function() {
    var currentData = {
        loggedIn: false
    };

    function getCurrent() {
        return currentData;
    }

    // called when the user logs in with the user data
    function loggedIn(userData) {
        // the object is REPLACED to avoid race conditions, see explanation below
        currentData = angular.extend({loggedIn: true}, userData);
    }

    return {
        getCurrent: getCurrent,
        loggedIn: loggedIn
    };
});

拦截器跟踪currentData. 如果拦截器收到 HTTP 401 并且loggedIn标志是true,它会将标志更改为false并重定向到登录视图。如果拦截器收到 HTTP 401 并且loggedIn标志是false,它除了拒绝请求之外什么都不做,因为另一个拦截器已经完成了视图重定向。

用户登录时currentData替换 ,避免出现响应延迟的情况(如调用call1和call2,call1响应401;call2也响应401,但延迟实际响应的传递;然后用户登录再次进入;然后 call2 收到其 401;第二个 401 不应覆盖当前状态)

App.config(["$provide", "$httpProvider", function($provide, $httpProvider) {
    $provide.factory("myHttpInterceptor", ["$q", "userData", "$cookieStore", "toastr", "$location",
        function($q, userData, $cookieStore, toastr, $location) {
            return {
                request: function(config) {
                    config.currentUserData = userData.getCurrent();
                    return config;
                },
                responseError: function(rejection) {
                    if( rejection && rejection.status === 401 && rejection.config && rejection.config.currentUserData && rejection.config.currentUserData.loggedIn ) {
                        rejection.config.currentUserData.loggedIn = false;
                        $cookieStore.remove('_angular_devise_user');
                        toastr.warning('You are logged out');
                        $location.path('/#/sign_in');
                    }
                    return $q.reject(rejection);
                }
            };
        }
    ]);

    $httpProvider.interceptors.push("myHttpInterceptor");
});

另请注意,我正在使用更新的方法来注册拦截器,这$httpProvider.responseInterceptors似乎已被弃用。

于 2013-09-18T11:16:10.573 回答