I need two instances of AngularJS $http service or what?

后端 未结 2 1347
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-30 00:49

I want add a response interceptor to my $http service for error handling purposes. The interceptor logic include send errors messages to server using $http in case necessary

相关标签:
2条回答
  • 2020-11-30 01:18

    1. Circular dependency problem.

    So, why does the error appear? Here is a quick overview of the process:

    1. $http service is requested.
    2. $httpProvider is asked to construct it.
    3. During construction you register interceptor, that requests $http service not existing yet.
    4. You get "Circular dependency" error.


    First solution.

    Create your dependency using angular.injector(). Notice, that you will create another $http service, independent from your app.

    $httpProvider.interceptors.push(function($q) {
        $injector = angular.injector();
        return {
            response: function(response) {
                $injector.invoke(function($http) {
                    // This is the exterior $http service!
                    // This interceptor will not affect it.
                });
            }
        };
    });
    


    Second solution (better).

    Inject $injector in your interceptor and use it to retrieve dependencies after $http initialization, right at the time you need them. These dependencies are registered services of your app and will not be created anew!

    $httpProvider.interceptors.push(function($q, $injector) {
        return {
            response: function(response) {
                $injector.invoke(function($http, someService) {
                    // $http is already constructed at the time and you may
                    // use it, just as any other service registered in your
                    // app module and modules on which app depends on.
                });
            }
        };
    });
    


    2. Interception prevention problem.

    If you use the second solution, there are actually two problems:

    1. If you utilize $http service inside your interceptor, you may end up with infinite interceptions: you send request, interceptor catches it, sends another, catches another, send again, and so on.
    2. Sometimes you want just prevent request from being intercepted.

    The 'config' parameter of $http service is just an object. You may create a convention, providing custom parameters and recognizing them in your interceptors.

    For example, let's add "nointercept" property to config and try duplicate every user request. This is a silly application, but useful example to understand the behavior:

    $httpProvider.interceptors.push(function($q, $injector) {
        return {
            response: function(response) {
                if (response.config.nointercept) {
                    return $q.when(response); // let it pass
                } else {
                    var defer = $q.defer();
                    $injector.invoke(function($http) {
                        // This modification prevents interception:
                        response.config.nointercept = true;
                        // Reuse modified config and send the same request again:
                        $http(response.config)
                            .then(function(resp) { defer.resolve(resp); },
                                  function(resp) { defer.reject(resp); });
                    });
                    return defer.promise;
                }
            }
        };
    });
    

    Having the testing for property in interceptor, you may prevent the interception in controllers and services:

    app.controller('myController', function($http) {
        // The second parameter is actually 'config', see API docs.
        // This query will not be duplicated by the interceptor.
        $http.get('/foo/bar', {nointercept: true})
            .success(function(data) {
                // ...
            });
    
    });
    
    0 讨论(0)
  • 2020-11-30 01:21

    I used what is described in the answer but I used the syntax with a factory because with the anonymous function it didn't work, I don't really know why:

    (function(angular){
        angular.module('app', [])
        .config([
            '$httpProvider',
            function($httpProvider) {
                    $httpProvider.interceptors.push('Interceptor');
            } 
        ])
        .factory('Interceptor', [
            '$injector',
            InterceptorFactory
        ]);
    
        function InterceptorFactory($injector){
    
            return {
                request: function(config) {             
                    var ServiceWithHttp = $injector.get('ServiceWithHttp');
                    // Use ServiceWithHttp
                    return config;
                }
            };
        }
    
    }(window.angular));
    
    0 讨论(0)
提交回复
热议问题