Angularjs 如何在响应拦截器中再次发送请求?

Angularjs 如何在响应拦截器中再次发送请求?,angularjs,interceptor,Angularjs,Interceptor,我在应用程序中制作了一个侦听器,用于检测会话丢失(服务器发送HTTP 419)。在这种情况下,我需要从服务器请求一个新会话,然后我想再次自动发送原始请求。 也许我可以将请求保存在请求拦截器中,然后再次发送,但可能有一个更简单的解决方案 请注意,我必须使用特定的Web服务来创建会话 angular.module('myapp', [ 'ngResource' ]).factory( 'MyInterceptor', function ($q, $rootScope) {

我在应用程序中制作了一个侦听器,用于检测会话丢失(服务器发送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');
    });

您的路径是正确的,您基本上将请求存储在队列中,并在建立会话后重试


查看这个流行的模块:AngularHTTPAuth()。在本模块中,他们截取401个响应,但您可以根据此方法对解决方案进行建模。

以下是我的解决方案,为感兴趣的人提供承诺。基本上,您需要请求一个新会话,并等待响应,然后再发送与原始请求相对应的新请求(使用response.config)。通过返回promise$http(response.config),您可以确保响应将被视为原始请求。
(语法可能不是最好的,因为我不熟悉承诺)


大致相同的解决方案,翻译成typescript:

/// <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)
    }]);
//
/// 
类authorInterceptorService{
静态serviceId:string=“authInterceptorService”;
构造函数(private$q:ng.IQService,private$location:ng.ILocationService,private$injector,private$log:ng.ILogService,private authStatusService){}
//注意,在这一点上,我们不需要任何额外的动力。
公共请求=(config:ng.IRequestConfig)=>{
config.headers=config.headers | |{};
var s:AuthStatus=this.authStatusService.status;
如果(s.isAuth){
config.headers.Authorization='Bearer'+s.accessToken;
}
返回配置;
}
公共响应错误=(拒绝:ng.ihttpromisecallbackarg)=>{
如果(拒绝状态===401){
var=这个;
此.log.warn(“[AuthInterceptorService.responseError()]:未授权请求[401]。现在我尝试刷新令牌。”);
var authService:authService=this.$injector.get(“authService”);
var$http:ng.IHttpService=this.$injector.get(“$http”);
var defer=this.$q.defer();
var promise:ng.IPromise=defer.promise.then(()=>$http(rejection.config));
授权服务
.refreshAccessToken()
。然后((响应)=>{
.log.info(“[AuthInterceptorService.responseError()]:令牌已成功刷新。现在我重新发送原始请求。”);
defer.resolve();
},
(错误)=>{
.log.warn(“[AuthInterceptorService.responseError()]:令牌刷新失败。我需要注销,抱歉…”);
this.authStatusService.clear();
此.$location.path('/login');
});
回报承诺;
}
退回此项。$q.拒绝(拒绝);
}
}
//将应用程序变量名称更新为模块变量的名称
应用程序工厂(authorInterceptorService.serviceId,
[“$q”、“$location”、“$injector”、“$log”、“authStatusService”、($q、$location、$injector、$log、authStatusService)=>{
返回新的AuthInterceptorService($q、$location、$injector、$log、authStatusService)
}]);

希望能有所帮助。

httpInterceptor的
responseError
方法必须如下所示:

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);
}

好的,谢谢你的链接,这很有趣。看起来$http(response.config)正在工作。我要试试。谢谢你让我开心!今天,请大家注意:Angular的拦截器语法自v1.1.x以来已经发生了变化。这个问题的解决方案是类似的,但是我们应该在这里看到更新的语法:
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);
}