22

我想为我的 $http 服务添加一个响应拦截器以进行错误处理。拦截器逻辑包括在必要时使用 $http 向服务器发送错误消息,我不想向服务器发送有关错误消息的错误消息,我的意思是,我想在向服务器发送错误消息时禁用我的拦截器。

我的想法是创建一个名为“remote_log”的服务,并将向服务器发送错误所需的所有代码放入其中。该服务当然将使用 $http 服务并将其包含在其依赖项列表中。

然后将拦截器的依赖添加到“remote_log”服务中,并在需要向服务器发送错误时使用拦截器内部的“remote_log”。问题在于:

当 $http 服务仍未实例化/可访问时,必须使用 $httpProvider 定义拦截器,因此,在拦截器内部代码不能依赖于 $http 服务,因为会发生“循环依赖”错误。

我认为我唯一的选择是在我的“remote_log”中创建一个单独的 $http 服务实例,该实例不使用我在创建拦截器时设置的 $httpProvider 配置。我的问题是:我该怎么做?还有其他想法吗?

4

2 回答 2

56

1.循环依赖问题。

那么,为什么会出现错误呢?以下是该过程的快速概述:

  1. 请求了 $http 服务。
  2. $httpProvider 被要求构建它。
  3. 在构建过程中,您注册了拦截器,该拦截器请求的 $http 服务尚不存在。
  4. 你得到“循环依赖”错误。


第一个解决方案。

使用 angular.injector() 创建你的依赖。请注意,您将创建另一个 $http 服务,独立于您的应用程序。

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


第二种解决方案(更好)。

在您的拦截器中注入 $injector 并在 $http 初始化后使用它来检索依赖项,就在您需要它们的时候。这些依赖项是您的应用程序的注册服务,不会重新创建!

$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.拦截预防问题。

如果使用第二种方案,其实有两个问题:

  1. 如果你在拦截器中使用 $http 服务,你可能会遇到无限的拦截:你发送请求,拦截器捕获它,发送另一个,捕获另一个,再次发送,等等。
  2. 有时您只想防止请求被拦截。

$http 服务的 'config' 参数只是一个对象。您可以创建一个约定,提供自定义参数并在您的拦截器中识别它们。

例如,让我们在配置中添加“nointercept”属性并尝试复制每个用户请求。这是一个愚蠢的应用程序,但对理解行为很有用:

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

在拦截器中测试属性,您可以防止在控制器和服务中拦截:

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) {
            // ...
        });

});
于 2013-11-13T13:02:58.843 回答
0

我使用了答案中描述的内容,但我使用了工厂的语法,因为匿名函数不起作用,我真的不知道为什么:

(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));
于 2015-08-03T10:32:39.617 回答