web-dev-qa-db-fra.com

L'injection de $ state (ui-router) dans l'intercepteur $ http provoque une dépendance circulaire

Ce que j'essaie de réaliser

J'aimerais passer à un certain état (connexion) au cas où une demande $ http renvoie une erreur 401. J'ai donc créé un intercepteur $ http.

Le problème

Lorsque j'essaie d'insérer '$ state' dans l'intercepteur, j'obtiens une dépendance circulaire. Pourquoi et comment je le répare?

Code

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

Le correctif

Utilisez le $injector service pour obtenir une référence au $state un service.

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 cause

angular-ui-router injecte le $http service comme dépendance à $TemplateFactory qui crée ensuite une référence circulaire à $http dans le $httpProvider lui-même lors de l'envoi de l'intercepteur.

La même exception de dépendance circulaire serait levée si vous essayez d’injecter le $http service directement dans un intercepteur comme tel.

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

Séparation des préoccupations

Des exceptions de dépendance circulaires peuvent indiquer qu'il existe dans votre application une combinaison de problèmes pouvant entraîner des problèmes de stabilité. Si vous vous trouvez avec cette exception, vous devriez prendre le temps de regarder votre architecture pour éviter les dépendances qui finissent par se référencer.

La réponse de Stephen Friedrich

Je suis d'accord avec la réponse ci-dessous que l'utilisation de la $injector obtenir directement une référence au service souhaité n’est pas idéal et pourrait être considéré comme un anti-modèle.

L'émission d'un événement est une solution beaucoup plus élégante et découplée.

212
Jonathan Palumbo

La question est un doublon de AngularJS: Injection de service dans un intercepteur HTTP (dépendance circulaire)

Je republie ma réponse à partir de ce fil ici:

Une meilleure solution

Je pense que l’utilisation directe de $ injector est un anti-modèle.

Un moyen de rompre la dépendance circulaire consiste à utiliser un événement: au lieu d'injecter $ state, injectez $ rootScope. Au lieu de rediriger directement, faites

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

plus

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

De cette façon, vous avez séparé les préoccupations:

  1. Détecter une réponse 401
  2. Rediriger pour vous connecter
25

La solution de Jonathan était géniale jusqu'à ce que j'essaye de sauver l'état actuel. Dans i-router v0.2.1 , l'état actuel ne semble pas être rempli lors du chargement initial de la page dans l'intercepteur.

Quoi qu'il en soit, je l'ai résolu en utilisant plutôt l'événement $ stateChangeError . L'événement $ stateChangeError vous donne à la fois à et de états, ainsi que l'erreur. C'est plutôt chouette.

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