Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: Injecting service into a HTTP interceptor (Circular dependency)

People also ask

Does AngularJS support dependency injection?

Dependency Injection is pervasive throughout AngularJS. You can use it when defining components or when providing run and config blocks for a module.


This is what I ended up doing

  .config(['$httpProvider', function ($httpProvider) {
        //enable cors
        $httpProvider.defaults.useXDomain = true;

        $httpProvider.interceptors.push(['$location', '$injector', '$q', function ($location, $injector, $q) {
            return {
                'request': function (config) {

                    //injected manually to get around circular dependency problem.
                    var AuthService = $injector.get('Auth');

                    if (!AuthService.isAuthenticated()) {
                        $location.path('/login');
                    } else {
                        //add session_id as a bearer token in header of all outgoing HTTP requests.
                        var currentUser = AuthService.getCurrentUser();
                        if (currentUser !== null) {
                            var sessionId = AuthService.getCurrentUser().sessionId;
                            if (sessionId) {
                                config.headers.Authorization = 'Bearer ' + sessionId;
                            }
                        }
                    }

                    //add headers
                    return config;
                },
                'responseError': function (rejection) {
                    if (rejection.status === 401) {

                        //injected manually to get around circular dependency problem.
                        var AuthService = $injector.get('Auth');

                        //if server returns 401 despite user being authenticated on app side, it means session timed out on server
                        if (AuthService.isAuthenticated()) {
                            AuthService.appLogOut();
                        }
                        $location.path('/login');
                        return $q.reject(rejection);
                    }
                }
            };
        }]);
    }]);

Note: The $injector.get calls should be within the methods of the interceptor, if you try to use them elsewhere you will continue to get a circular dependency error in JS.


You have a circular dependency between $http and your AuthService.

What you are doing by using the $injector service is solving the chicken-and-egg problem by delaying the dependency of $http on the AuthService.

I believe that what you did is actually the simplest way of doing it.

You could also do this by:

  • Registering the interceptor later (doing so in a run() block instead of a config() block might already do the trick). But can you guarantee that $http hasn't been called already?
  • "Injecting" $http manually into the AuthService when you're registering the interceptor by calling AuthService.setHttp() or something.
  • ...

I think using the $injector directly is an antipattern.

A way to break the circular dependency is to use an event: Instead of injecting $state, inject $rootScope. Instead of redirecting directly, do

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

plus

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

Bad logic made such results

Actually there is no point of seeking is user authored or not in Http Interceptor. I would recomend to wrap your all HTTP requests into single .service (or .factory, or into .provider), and use it for ALL requests. On each time you call function, you can check is user logged in or not. If all is ok, allow send request.

In your case, Angular application will send request in any case, you just checking authorization there, and after that JavaScript will send request.

Core of your problem

myHttpInterceptor is called under $httpProvider instance. Your AuthService uses $http, or $resource, and here you have dependency recursion, or circular dependency. If your remove that dependency from AuthService, than you will not see that error.


Also as @Pieter Herroelen pointed, you could place this interceptor in your module module.run, but this will be more like a hack, not a solution.

If your up to do clean and self descriptive code, you must go with some of SOLID principles.

At least Single Responsibility principle will help you a lot in such situations.