Ввод $state (ui-router) в $http-перехватчик вызывает циклическую зависимость

Что я пытаюсь достичь

Я хотел бы перейти к определенному состоянию (логину), если запрос $http возвращает ошибку 401. Поэтому я создал перехватчик $http.

Проблема

Когда я пытаюсь вставить '$ state' в перехватчик, я получаю циклическую зависимость. Почему и как это исправить?

код

//Inside Config function

    var interceptor = ['$location', '$q', '$state', function($location, $q, $state) {
        function success(response) {
            return response;
        }

        function error(response) {

            if(response.status === 401) {
                $state.transitionTo('public.login');
                return $q.reject(response);
            }
            else {
                return $q.reject(response);
            }
        }

        return function(promise) {
            return promise.then(success, error);
        }
    }];

    $httpProvider.responseInterceptors.push(interceptor);

Ответ 1

Исправление

Используйте службу $injector, чтобы получить ссылку на службу $state.

var interceptor = ['$location', '$q', '$injector', function($location, $q, $injector) {
    function success(response) {
        return response;
    }

    function error(response) {

        if(response.status === 401) {
            $injector.get('$state').transitionTo('public.login');
            return $q.reject(response);
        }
        else {
            return $q.reject(response);
        }
    }

    return function(promise) {
        return promise.then(success, error);
    }
}];

$httpProvider.responseInterceptors.push(interceptor);

Причина

angular -ui-router внедряет службу $http как зависимость в $TemplateFactory, которая затем создает круговую ссылку на $http внутри самого $httpProvider при отправке перехватчика.

Такое же исключение циклической зависимости будет выбрано, если вы попытаетесь вставить службу $http непосредственно в такой перехватчик.

var interceptor = ['$location', '$q', '$http', function($location, $q, $http) {

Разделение проблем

Исключения циклических зависимостей могут указывать на то, что в вашем приложении происходит смешение проблем, которые могут вызвать проблемы с стабильностью. Если вы обнаружите это исключение, вы должны потратить время, чтобы посмотреть на свою архитектуру, чтобы избежать каких-либо зависимостей, которые в конечном итоге ссылаются на самих себя.

@Стефен Фридрих отвечает

Я согласен с нижеприведенным ответом, что использование $injector для прямого получения ссылки на желаемую услугу не является идеальным и может рассматриваться как анти-шаблон.

Излучение события является гораздо более элегантным и также развязанным решением.

Ответ 2

Вопрос - это дубликат AngularJS: услуга инжекции в HTTP-перехватчик (круговая зависимость)

Я снова отправляю свой ответ из этой темы:

Лучшее исправление

Я думаю, что использование инжектора $напрямую является антипатерном.

Способ разбить круговую зависимость - использовать событие: Вместо того, чтобы вводить $state, введите $rootScope. Вместо прямого перенаправления do

this.$rootScope.$emit("unauthorized");

плюс

angular
    .module('foo')
    .run(function($rootScope, $state) {
        $rootScope.$on('unauthorized', () => {
            $state.transitionTo('login');
        });
    });

Таким образом вы отделили проблемы:

  • Обнаружение ответа 401
  • Перенаправление для входа в систему

Ответ 3

Решение Джонатана было здорово, пока я не попытался сохранить текущее состояние. В ui-router v0.2.10 текущее состояние, похоже, не заполняется при начальной загрузке страницы в перехватчике.

В любом случае, я решил это, используя $stateChangeError. Событие $stateChangeError предоставляет вам как , так и из, а также ошибку. Это довольно изящно.

$rootScope.$on('$stateChangeError',
    function(event, toState, toParams, fromState, fromParams, error){
        console.log('stateChangeError');
        console.log(toState, toParams, fromState, fromParams, error);

        if(error.status == 401){
            console.log("401 detected. Redirecting...");

            authService.deniedState = toState.name;
            $state.go("login");
        }
    });