Monitoring $http Activity With $http Interceptors In AngularJS
After experimenting with the $provide.decorator() method in AngularJS, I wanted to try looking at another form of decorator (of sorts) - an interceptor. For a long time, AngularJS has allowed us to intercept $http responses. Then, in AngularJS 1.1.4, they extended interceptor functionality to include both outgoing requests and incoming responses. Now, with our ability to straddle both sides of the HTTP fence, I wanted to see if I could use interceptors to track and monitor HTTP requests within an AngularJS application.
Like the $provide.decorator() in my previous blog post, the $http request interceptors have to be setup during the configuration phase of the bootstrapping. The config phase is the only time we have access to the providers before the underlying services have been instantiated.
The entire $http request / response interceptor chain is implement as a Promise chain. The core $http request sits, as a promise, in the middle of the chain. Your request interceptors are then added before the core request while your response interceptors are added after the core request:
It's quite beautiful, isn't it? Using interceptors, we should be able to tally up all the outgoing requests and then decrement pending requests on the way back. In practice, this actually becomes quite easy.
In theory, however, this is problematic. See, in theory, you could have a competing request interceptor or a response interceptor that completely modifies the config object, or throws an error making the config object unavailable. In such - albeit outlier edge-cases - it will become very difficult (in an elegant way) to reconcile an outgoing request with an incoming response.
REALITY CHECK: Now, again, in practice, I want to stress that this will likely never happen. In practice, you probably only have one interceptor (if that) and know exactly what it's doing. The complexity would really only occur in cases where you're using a third-party module that has implemented an $http interceptor that is doing something silly and unfortunate.
That said, I wanted to try to and explore $http interceptors in a way that makes the code a bit more defensive. In the following demo, I have two main blocks - the trafficCop service which manages the tally of HTTP request activity and, the config() block which sets up the interceptor that pipes $http activity into the trafficCop service:
As you can see, the bulk of the code deals with the "what if" situations. What if the request is rejected? What if the response doesn't have a config object? What if the response config method doesn't match a pending method? In reality, none of these "what ifs" would ever occur. But, the mechanism provided by the promise chain means that they "could." And, I thought it would make the exploration more interesting.
And, when we run the above code, we get the following console output:
The more I dig into AngularJS, the more I learn to love Promises. And, with the way that AngularJS wraps $http requests in a promise chain, it gives us an opportunity to track outgoing and incoming HTTP activity.
Want to use code from this post? Check out the license.
If you add an object to config in the request interceptor, you will find it again in the response config... That's very useful to track which request is which.
I use it to store a promise that I resolve in the response interceptor, so I can automatically queue requests (my server is not really configured to handle async requests but fast enough so that the user usually doesn't notice, saves me some headaches)
BTW I love your articles on Angular, I always learn a lot.
Super interesting - queuing up requests. I would have never thought of that. I am wondering what kind of server you have that you need to do that. Just interested.
In practice, altering the config should be fine. But, in *theory*, another interceptor that executes after yours could... *theoretically* replace the entire config object before the request even goes out. Obviously, that will never happen; but, there's nothing about promises NOR the documentation that said that it "shouldn't". In fact, the documentation for $http says that you can replace the config object entirely.
Obviously, we're just talking philosophy at this point, but I wish the docs had a strong opinion on what a valid use-case was. Then at least we could point-fingers :P
Thanks for the kind words - glad you like my exploration :D
My first use case for using interceptors was when I wanted to add a timestamp URL parameter for each GET request made for retrieving data (GET requests for HTML content would be unaffected) to circumvent some of the caching I was seeing in IE 8 at the time.
What I realized later was that I needed a way to "turn off" that behavior during unit testing, otherwise my $httpBackend GET request mocks wouldn't work without a timestamped URL (doable with regex, but a bit of a pain). So I always make sure I inject some object into the interceptors whose state conditionally affects what does and doesn't happen within the interceptor function.
Thanks for the idea of queuing request, I never thought of that kind of things can be done. But..
How the modified config data is sent back in the response, should we need to capture it and attach it to the response from server side or is there any magic I don't understand.??
That helped me so much :)
This would be useful if your server was performing some blocking calls to a single-threaded third-party routine. If you let all available requests hit a web server that is processing blocking calls, each thread will be blocked waiting on its response.
1. At some point, all available threads may get consumed with these blocking calls.
2. All the context-switching that goes on scheduling each thread its time to run when those threads are just waiting, is just wasted activity taking CPU cycles away from the overall application... and yes, there may be a way to make each call such that it will be queued.
It is a much better utilization of resources to allow one blocking call at a time and let it finish before sending another.
Great article, BTW, how to calculate the total processing time. you know, browser will queue all your requests and send 6-8 requests each time, then one finished, another get out, so how to calculate a total time from all async requests?
Brilliant. That;s exactly what I need to do! Thx. Now I know interceptors can do that, I know I'm on the right path.
Look above in the comments about how Anne-Laure uses interceptors. I'm guessing you can put a timestamp. With Anne-Laure's method I'm sure you can add a time property to the object in the request interceptor. You'll have access to this object and therefore the time property in the response.
Where's the "that's me" in the header photo...?
Thanks for the information. This will aid me in understanding promises more deeply.