Ввод $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");
}
});