【问题标题】:I need two instances of AngularJS $http service or what?我需要 AngularJS $http 服务的两个实例还是什么?
【发布时间】:2024-05-17 21:30:02
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 如果您共享代码的相关 sn-ps 将会很有用

标签: http service error-handling angularjs interceptor


【解决方案1】:

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

});

【讨论】:

  • 这为我节省了很多时间。感谢您的良好解释,并花时间讨论该方法的问题。
  • 这应该在官方文档中。官方文档演示了注入任意依赖项,这对我来说非常非常糟糕。谢谢@Thaumant。
  • 只是重申上面的人所说的话,感谢您加倍努力并正确解释这一点
  • 我正在注入我的服务,例如: angular.module('app.utils').factory('AuthInterceptor', ['$q', '$state', function($q, $state ) { 工作得很好,但是在升级 AngularJS 之后,它给了我 $state 服务的循环依赖问题。它以前工作过,现在它坏了。这个解决方案很有趣,但它迫使我们为每个请求注入和响应类型。我希望我能保持我的代码不变。
  • @thaumant 我尝试应用第一个和第二个解决方案,但出现语法错误,有什么帮助吗? :(
【解决方案2】:

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

(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));

【讨论】: