Necesito dos instancias del servicio AngularJS $ http o qué?

Quiero agregar un interceptor de respuesta a mi servicio $ http para el manejo de errores. La lógica del interceptor incluye enviar mensajes de errores al servidor usando $ http en caso de ser necesario, PERO no deseo enviar mensajes de error al servidor sobre mensajes de error, quiero decir, quiero desactivar mi interceptor mientras envío un mensaje de error al servidor.

Mi idea era crear un servicio llamado ‘remote_log’ y poner dentro de él todo el código necesario para enviar un error al servidor. Ese servicio, por supuesto, usará el servicio $ http y lo tendrá en su lista de dependencias.

A continuación, agregue como dependencia del interceptor al servicio ‘remote_log’, y use ‘remote_log’ dentro del interceptor cuando necesite enviar errores al servidor. El problema es que:

Los interceptores se deben definir utilizando el $ httpProvider cuando el servicio $ http aún no está instanciado / accesible, por lo tanto, dentro del código interceptor no puede haber una dependencia al servicio $ http porque ocurre un error de “Dependencia circular”.

Creo que mi única opción es crear una instancia separada del servicio $ http dentro de mi ‘remote_log’, una instancia que no usa la configuración $ httpProvider que establecí al crear el interceptor. Mi pregunta es: ¿cómo puedo hacer eso? ¿Alguna otra idea?

1. Problema de dependencia circular.

Entonces, ¿por qué aparece el error? Aquí hay una descripción general rápida del proceso:

  1. Se solicita $ http.
  2. $ httpProvider se le pide que lo construya.
  3. Durante la construcción, registra el interceptor, que solicita que el servicio $ http aún no exista.
  4. Obtiene el error de “Dependencia circular”.

Primera solución

Crea tu dependencia usando angular.injector (). Tenga en cuenta que creará otro servicio $ http independiente de su aplicación.

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

Segunda solución (mejor).

Inyecte $ inyector en su interceptor y úselo para recuperar dependencias después de la inicialización de $ http, justo en el momento en que los necesita. Estas dependencias son servicios registrados de tu aplicación y no se crearán de nuevo.

 $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. Problema de prevención de interceptación.

Si usa la segunda solución, en realidad hay dos problemas:

  1. Si utiliza el servicio $ http dentro de su interceptor, puede terminar con infinitas interceptaciones: envía una solicitud, el interceptor la atrapa, envía otra, atrapa otra, la envía de nuevo, y así sucesivamente.
  2. A veces lo que desea es evitar que la solicitud sea interceptada.

El parámetro ‘config’ del servicio $ http es solo un objeto. Puede crear una convención, proporcionar parámetros personalizados y reconocerlos en sus interceptores.

Por ejemplo, agreguemos la propiedad “nointercept” a la configuración e intentemos duplicar cada solicitud del usuario. Esta es una aplicación tonta, pero un ejemplo útil para comprender el comportamiento:

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

Al realizar la prueba de la propiedad en el interceptor, puede evitar la intercepción en los controladores y servicios:

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

Usé lo que se describe en la respuesta pero utilicé la syntax con una fábrica porque con la función anónima no funcionó, realmente no sé por qué:

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