Inyectar $ state (ui-router) en el interceptor http $ provoca una dependencia circular

Lo que bash lograr

Me gustaría pasar a un estado determinado (inicio de sesión) en caso de que una solicitud $ http devuelva un error 401. Por lo tanto, he creado un interceptor $ http.

El problema

Cuando bash insertar ‘$ state’ en el interceptor, obtengo una dependencia circular. ¿Por qué y cómo lo arreglo?

Código

//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 solución

Use el servicio $injector para obtener una referencia al servicio $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 inyecta el servicio $http como una dependencia en $TemplateFactory que luego crea una referencia circular a $http dentro del $httpProvider al enviar el interceptor.

La misma excepción de dependencia circular se generaría si intentas inyectar el servicio $http directamente en un interceptor como ese.

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

Separación de intereses

Las excepciones de dependencia circular pueden indicar que hay una mezcla de preocupaciones dentro de su aplicación que podría causar problemas de estabilidad. Si se encuentra con esta excepción, debe tomarse el tiempo de observar su architecture para asegurarse de evitar cualquier dependencia que termine haciendo referencia a ellos.

@Stephen la respuesta de Friedrich

Estoy de acuerdo con la respuesta a continuación de que usar $injector para obtener directamente una referencia del servicio deseado no es ideal y podría considerarse un anti patrón.

Emitir un evento es una solución mucho más elegante y también desacoplada.

La pregunta es un duplicado de AngularJS: servicio de inyección en un interceptor HTTP (dependencia circular)

Estoy volviendo a publicar mi respuesta de ese hilo aquí:

Una mejor solución

Creo que usar $ inyector directamente es un antipatrón.

Una forma de romper la dependencia circular es usar un evento: en lugar de inyectar $ state, inyecte $ rootScope. En lugar de redireccionar directamente, haga

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

más

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

De esa manera has separado las preocupaciones:

  1. Detecta una respuesta 401
  2. Redirigir para iniciar sesión

La solución de Jonathan fue excelente hasta que intenté guardar el estado actual. En ui-router v0.2.10, el estado actual no parece estar poblado en la carga de la página inicial en el interceptor.

De todos modos, lo resolví usando el evento $ stateChangeError en su lugar. El evento $ stateChangeError le proporciona estados tanto de ida como de vuelta , así como también el error. Es bastante ingenioso.

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