I need two instances of AngularJS $http service or what?
1. Circular dependency problem.
So, why does the error appear? Here is a quick overview of the process:
- $http service is requested.
- $httpProvider is asked to construct it.
- During construction you register interceptor, that requests $http service not existing yet.
- 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:
- If you utilize $http service inside yourinterceptor, you may end up with infinite interceptions: you sendrequest, interceptor catches it, sends another, catches another,send again, and so on.
- 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) { // ... });});
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));