L’iniezione di $ state (ui-router) in $ http intercettore causa una dipendenza circolare

Quello che sto cercando di ottenere

Vorrei passare a un determinato stato (login) nel caso in cui una richiesta $ http restituisca un errore 401. Ho quindi creato un intercettore $ http.

Il problema

Quando sto cercando di inserire “$ stato” nell’intercettore ottengo una dipendenza circolare. Perché e come lo aggiusto?

Codice

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

La correzione

Utilizzare il servizio $injector per ottenere un riferimento al servizio $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); 

La causa

angular-ui-router inietta il servizio $http come dipendenza in $TemplateFactory che crea quindi un riferimento circolare a $http all’interno del $httpProvider stesso al momento della distribuzione dell’intercettatore.

La stessa eccezione di dipendenza circolare verrebbe generata se si tenta di iniettare il servizio $http direttamente in un intercettore in questo modo.

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

Separazione degli interessi

Eccezioni circolari di dipendenza possono indicare che esiste una combinazione di problemi all’interno dell’applicazione che potrebbe causare problemi di stabilità. Se ti trovi con questa eccezione, dovresti avere il tempo di guardare la tua architettura per assicurarti di evitare qualsiasi dipendenza che finisca per riferirsi.

@Stephen Friedrich’s answer

Sono d’accordo con la risposta qui sotto che usare l’ $injector per ottenere direttamente un riferimento al servizio desiderato non è l’ideale e potrebbe essere considerato un modello anti.

Emettere un evento è una soluzione molto più elegante e disaccoppiata.

La domanda è un duplicato di AngularJS: Iniezione di servizi in un intercettore HTTP (dipendenza circolare)

Sto postando la mia risposta da quel thread qui:

Una soluzione migliore

Penso che usare $ injector direttamente sia un antipattern.

Un modo per interrompere la dipendenza circolare è utilizzare un evento: invece di iniettare $ state, iniettare $ rootScope. Invece di redirect direttamente, fallo

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

più

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

In questo modo hai separato le preoccupazioni:

  1. Rileva una risposta 401
  2. Reindirizza per accedere

La soluzione di Jonathan è stata fantastica fino a quando non ho provato a salvare lo stato attuale. In ui-router v0.2.10 lo stato corrente non sembra essere popolato sul caricamento iniziale della pagina nell’intercettore.

Ad ogni modo, l’ho risolto utilizzando l’evento $ stateChangeError . L’evento $ stateChangeError fornisce sia gli stati da e verso gli stati, sia l’errore. È piuttosto carino.

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