2013-09-05 6 views
29

Nella mia applicazione ho intercettato un intercettore che rileva la perdita di sessione (il server invia un HTTP 419). In questo caso, ho bisogno di richiedere una nuova sessione dal server, e quindi vorrei inviare nuovamente la richiesta originale automaticamente.
Forse potrei salvare la richiesta in un intercettore di richiesta, e quindi inviarlo di nuovo, ma potrebbe esserci una soluzione più semplice.Come posso inviare nuovamente la richiesta nell'interceptor di risposta?

Nota che per creare la sessione devo utilizzare uno specifico webservice.

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

risposta

21

Ecco la mia soluzione che utilizza le promesse per chi è interessato. Fondamentalmente è necessario richiedere una nuova sessione e attendere la risposta prima di inviare una nuova richiesta corrispondente alla richiesta originale (utilizzando response.config). Restituendo la promessa $ http (response.config) si assicura che la risposta venga trattata come se fosse la richiesta originale.
(sintassi può non essere la migliore, come io sono nuovo alle promesse)

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

Una nota per chi la guarda oggi: la sintassi di Angular per gli intercettori è cambiata dalla v1.1.x. La soluzione alla domanda è analoga, ma si dovrebbe vedere qui per la più recente sintassi: https://docs.angularjs.org/api/ng/service/$http#interceptors –

+0

Ecco una versione semplificata di questo: http://stackoverflow.com/a/31965945/651556 – Sushil

5

Sei sulla strada giusta, in pratica si salva la richiesta in coda e si riprova dopo aver ristabilito la sessione.

Dai un'occhiata a questo modulo popolare: auth http angolare (https://github.com/witoldsz/angular-http-auth). In questo modulo, intercettano le risposte 401 ma è possibile modellare la soluzione da questo approccio.

+0

Ok grazie per il link è interessante. Sembra che $ http (response.config) funzioni. Lo testerò. – D0m3

3

Più o meno la stessa soluzione, tradotto a macchina:

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

Spero che questo aiuto.

17

Il responseError metodo httpInterceptor devono essere come questo:

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

E la magia accade qui:

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

Grazie per aver reso la mia giornata! –

+0

Brillante, grazie. – brazorf