2013-09-05 94 views
29

我在应用程序中制作了一个检测会话丢失的拦截器(服务器发送HTTP 419)。在这种情况下,我需要从服务器请求一个新会话,然后我想再次自动发送原始请求。
也许我可以将请求保存在请求拦截器中,然后再发送一次,但可能有一个更简单的解决方案。如何在响应拦截器中再次发送请求?

请注意,我必须使用特定的web服务来创建会话。

angular.module('myapp', [ 'ngResource' ]).factory(
    'MyInterceptor', 
    function ($q, $rootScope) { 
     return function (promise) { 
      return promise.then(function (response) { 
       // do something on success 
       return response; 
      }, function (response) { 
       if(response.status == 419){ 
        // session lost 
        // create new session server-side 
        // Session.query(); 
        // then send current request again 
        // ??? 
       } 
       return $q.reject(response); 
      }); 
     }; 
    }).config(function ($httpProvider) { 
     $httpProvider.responseInterceptors.push('MyInterceptor'); 
    }); 

回答

21

这是我对有兴趣的人使用promise的解决方案。基本上你需要请求一个新的会话,并在发送与原始请求相对应的新请求之前等待响应(使用response.config)。通过返回承诺$ http(response.config),确保将响应视为原始请求。
(语法可能不是最好的,因为我是新来的承诺)

angular.module('myapp', [ 'ngResource' ]).factory(
    'MyInterceptor', 
    function ($q, $rootScope) { 
     return function (promise) { 
      return promise.then(function (response) { 
       // do something on success 
       return response; 
      }, function (response) { 
       if(response.status == 419){ 
        // session lost 
        var Session = $injector.get('Session'); 
        var $http = $injector.get('$http'); 
        // first create new session server-side 
        var defer = $q.defer(); 
        var promiseSession = defer.promise; 
        Session.query({},function(){ 
         defer.resolve(); 
        }, function(){ 
         // error 
         defer.reject(); 
        });  
        // and chain request 
        var promiseUpdate = promiseSession.then(function(){ 
         return $http(response.config); 
        }); 
        return promiseUpdate; 
       } 
       return $q.reject(response); 
      }); 
     }; 
    }).config(function ($httpProvider) { 
     $httpProvider.responseInterceptors.push('MyInterceptor'); 
    }); 
+0

今天有人看到这个提示:Angular的拦截器语法从v1.1.x开始已经发生了变化。这个问题的解决方案是类似的,但应该在这里看到更新的语法:https://docs.angularjs.org/api/ng/service/$http#interceptors –

+0

下面是这个的简化版本:http:/ /stackoverflow.com/a/31965945/651556 – Sushil

5

你在正确的道路上,基本上将请求存储在队列中,并在重新建立会话后重试它。

看看这个流行的模块:angular http auth(https://github.com/witoldsz/angular-http-auth)。在这个模块中,他们拦截了401个响应,但您可以使用这种方法为您的解决方案建模。

+0

它是有趣的链接好,谢谢。看来$ http(response.config)正在工作。我要去测试一下。 – D0m3

3

或多或少相同的解决方案,在翻译打字稿:

/// <reference path="../app.ts" /> 
/// <reference path="../../scripts/typings/angularjs/angular.d.ts" /> 

class AuthInterceptorService { 

    static serviceId: string = "authInterceptorService"; 

    constructor(private $q: ng.IQService, private $location: ng.ILocationService, private $injector, private $log: ng.ILogService, private authStatusService) {} 

    // Attenzione. Per qualche strano motivo qui va usata la sintassi lambda perché se no ts sbrocca il this. 
    public request = (config: ng.IRequestConfig) => { 

     config.headers = config.headers || {}; 

     var s: AuthStatus = this.authStatusService.status; 
     if (s.isAuth) { 
      config.headers.Authorization = 'Bearer ' + s.accessToken; 
     } 

     return config; 
    } 

    public responseError = (rejection: ng.IHttpPromiseCallbackArg<any>) => { 

     if (rejection.status === 401) { 

      var that = this; 

      this.$log.warn("[AuthInterceptorService.responseError()]: not authorized request [401]. Now I try now to refresh the token."); 

      var authService: AuthService = this.$injector.get("authService"); 
      var $http: ng.IHttpService = this.$injector.get("$http"); 

      var defer = this.$q.defer(); 
      var promise: ng.IPromise<any> = defer.promise.then(() => $http(rejection.config)); 

      authService 
       .refreshAccessToken() 
        .then((response) => { 

         that.$log.info("[AuthInterceptorService.responseError()]: token refreshed succesfully. Now I resend the original request."); 

         defer.resolve(); 
        }, 
        (err) => { 

         that.$log.warn("[AuthInterceptorService.responseError()]: token refresh failed. I need to logout, sorry..."); 

         this.authStatusService.clear(); 
         this.$location.path('/login'); 
        }); 

      return promise; 
     } 

     return this.$q.reject(rejection); 
    } 
} 

// Update the app variable name to be that of your module variable 
app.factory(AuthInterceptorService.serviceId, 
    ["$q", "$location", "$injector", "$log", "authStatusService", ($q, $location, $injector, $log, authStatusService) => { 
     return new AuthInterceptorService($q, $location, $injector, $log, authStatusService) 
    }]); 

希望这有助于。

17

httpInterceptorresponseError方法必须是这样的:

responseError: function (response) { 
    // omit the retry if the request is made to a template or other url 
    if (response.config.apiCal === true) { 
    if (response.status === 419) { 
     var deferred = $q.defer(); 
     // do something async: try to login.. rescue a token.. etc. 
     asyncFuncionToRecoverFrom419(funcion(){ 
     // on success retry the http request 
     retryHttpRequest(response.config, deferred); 
     }); 
     return deferred.promise; 
    } else { 
     // a template file... 
     return response; 
    } 
    } 
} 

和魔术在这里发生:

function retryHttpRequest(config, deferred){ 
    function successCallback(response){ 
    deferred.resolve(response); 
    } 
    function errorCallback(response){ 
    deferred.reject(response); 
    } 
    var $http = $injector.get('$http'); 
    $http(config).then(successCallback, errorCallback); 
} 
+0

谢谢你让我的一天! –

+0

辉煌,谢谢。 – brazorf