Подтвердить что ты не робот

Как отправить запрос повторно в ответный перехватчик?

Я сделал перехватчик в своем приложении, которое обнаруживает потерю сеанса (сервер отправляет HTTP 419). В этом случае мне нужно запросить новый сеанс с сервера, а затем я бы хотел отправить исходный запрос снова автоматически.
Возможно, я мог бы сохранить запрос в перехватчике запросов, а затем отправить его снова, но может быть более простое решение.

Обратите внимание, что для создания сеанса я должен использовать определенный веб-сервис.

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

Ответ 1

Вот мое решение, использующее promises для заинтересованных. В основном вам нужно запросить новый сеанс и дождаться ответа перед отправкой нового запроса, соответствующего исходному запросу (с помощью response.config). Вернув обещание $http (response.config), вы убедитесь, что ответ будет обработан так, как если бы это был исходный запрос.
(синтаксис может быть не лучшим, поскольку я новичок в promises)

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

Ответ 2

Метод responseError httpInterceptor должен быть таким:

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

Ответ 3

Вы на правильном пути, вы в основном сохраняете запрос в очереди и повторите попытку после того, как вы снова установили сеанс.

Посмотрите этот популярный модуль: angular http auth (https://github.com/witoldsz/angular-http-auth). В этом модуле они перехватывают 401 ответ, но вы можете смоделировать свое решение от этого подхода.

Ответ 4

Более или менее одно и то же решение, переведенное в 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)
    }]);

Надеюсь на эту помощь.