@@ -155,20 +155,52 @@ function $HttpProvider() {
155
155
xsrfHeaderName : 'X-XSRF-TOKEN'
156
156
} ;
157
157
158
- var providerResponseInterceptors = this . responseInterceptors = [ ] ;
158
+ /**
159
+ * Are order by request. I.E. they are applied in the same order as
160
+ * array on request, but revers order on response.
161
+ */
162
+ var interceptorFactories = this . interceptors = [ ] ;
163
+ /**
164
+ * For historical reasons, response interceptors ordered by the order in which
165
+ * they are applied to response. (This is in revers to interceptorFactories)
166
+ */
167
+ var responseInterceptorFactories = this . responseInterceptors = [ ] ;
159
168
160
169
this . $get = [ '$httpBackend' , '$browser' , '$cacheFactory' , '$rootScope' , '$q' , '$injector' ,
161
170
function ( $httpBackend , $browser , $cacheFactory , $rootScope , $q , $injector ) {
162
171
163
- var defaultCache = $cacheFactory ( '$http' ) ,
164
- responseInterceptors = [ ] ;
172
+ var defaultCache = $cacheFactory ( '$http' ) ;
165
173
166
- forEach ( providerResponseInterceptors , function ( interceptor ) {
167
- responseInterceptors . push (
168
- isString ( interceptor )
169
- ? $injector . get ( interceptor )
170
- : $injector . invoke ( interceptor )
171
- ) ;
174
+ /**
175
+ * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
176
+ * The reversal is needed so that we can build up the interception chain around the
177
+ * server request.
178
+ */
179
+ var reversedInterceptors = [ ] ;
180
+
181
+ forEach ( interceptorFactories , function ( interceptorFactory ) {
182
+ reversedInterceptors . unshift ( isString ( interceptorFactory )
183
+ ? $injector . get ( interceptorFactory ) : $injector . invoke ( interceptorFactory ) ) ;
184
+ } ) ;
185
+
186
+ forEach ( responseInterceptorFactories , function ( interceptorFactory , index ) {
187
+ var responseFn = isString ( interceptorFactory )
188
+ ? $injector . get ( interceptorFactory )
189
+ : $injector . invoke ( interceptorFactory ) ;
190
+
191
+ /**
192
+ * Response interceptors go before "around" interceptors (no real reason, just
193
+ * had to pick one.) But they are already revesed, so we can't use unshift, hence
194
+ * the splice.
195
+ */
196
+ reversedInterceptors . splice ( index , 0 , {
197
+ response : function ( response ) {
198
+ return responseFn ( $q . when ( response ) ) ;
199
+ } ,
200
+ responseError : function ( response ) {
201
+ return responseFn ( $q . reject ( response ) ) ;
202
+ }
203
+ } ) ;
172
204
} ) ;
173
205
174
206
@@ -310,7 +342,90 @@ function $HttpProvider() {
310
342
* To skip it, set configuration property `cache` to `false`.
311
343
*
312
344
*
313
- * # Response interceptors
345
+ * # Interceptors
346
+ *
347
+ * Before you start creating interceptors, be sure to understand the
348
+ * {@link ng.$q $q and deferred/promise APIs}.
349
+ *
350
+ * For purposes of global error handling, authentication or any kind of synchronous or
351
+ * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
352
+ * able to intercept requests before they are handed to the server and
353
+ * responses before they are handed over to the application code that
354
+ * initiated these requests. The interceptors leverage the {@link ng.$q
355
+ * promise APIs} to fulfil this need for both synchronous and asynchronous pre-processing.
356
+ *
357
+ * The interceptors are service factories that are registered with the $httpProvider by
358
+ * adding them to the `$httpProvider.interceptors` array. The factory is called and
359
+ * injected with dependencies (if specified) and returns the interceptor.
360
+ *
361
+ * There are two kinds of interceptors (and two kinds of rejection interceptors):
362
+ *
363
+ * * `request`: interceptors get called with http `config` object. The function is free to modify
364
+ * the `config` or create a new one. The function needs to return the `config` directly or as a
365
+ * promise.
366
+ * * `requestError`: interceptor gets called when a previous interceptor threw an error or resolved
367
+ * with a rejection.
368
+ * * `response`: interceptors get called with http `response` object. The function is free to modify
369
+ * the `response` or create a new one. The function needs to return the `response` directly or as a
370
+ * promise.
371
+ * * `responseError`: interceptor gets called when a previous interceptor threw an error or resolved
372
+ * with a rejection.
373
+ *
374
+ *
375
+ * <pre>
376
+ * // register the interceptor as a service
377
+ * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
378
+ * return {
379
+ * // optional method
380
+ * 'request': function(config) {
381
+ * // do something on success
382
+ * return config || $q.when(config);
383
+ * },
384
+ *
385
+ * // optional method
386
+ * 'requestError': function(rejection) {
387
+ * // do something on error
388
+ * if (canRecover(rejection)) {
389
+ * return responseOrNewPromise
390
+ * }
391
+ * return $q.reject(rejection);
392
+ * },
393
+ *
394
+ *
395
+ *
396
+ * // optional method
397
+ * 'response': function(response) {
398
+ * // do something on success
399
+ * return response || $q.when(response);
400
+ * },
401
+ *
402
+ * // optional method
403
+ * 'responseError': function(rejection) {
404
+ * // do something on error
405
+ * if (canRecover(rejection)) {
406
+ * return responseOrNewPromise
407
+ * }
408
+ * return $q.reject(rejection);
409
+ * };
410
+ * }
411
+ * });
412
+ *
413
+ * $httpProvider.interceptors.push('myHttpInterceptor');
414
+ *
415
+ *
416
+ * // register the interceptor via an anonymous factory
417
+ * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
418
+ * return {
419
+ * 'request': function(config) {
420
+ * // same as above
421
+ * },
422
+ * 'response': function(response) {
423
+ * // same as above
424
+ * }
425
+ * });
426
+ * </pre>
427
+ *
428
+ * # Response interceptors (DEPRECATED)
314
429
*
315
430
* Before you start creating interceptors, be sure to understand the
316
431
* {@link ng.$q $q and deferred/promise APIs }.
@@ -526,45 +641,66 @@ function $HttpProvider() {
526
641
</file>
527
642
</example>
528
643
*/
529
- function $http ( config ) {
644
+ function $http ( requestConfig ) {
645
+ var config = {
646
+ transformRequest : defaults . transformRequest ,
647
+ transformResponse : defaults . transformResponse
648
+ } ;
649
+ var headers = { } ;
650
+
651
+ extend ( config , requestConfig ) ;
652
+ config . headers = headers ;
530
653
config . method = uppercase ( config . method ) ;
531
654
532
- var xsrfHeader = { } ,
533
- xsrfCookieName = config . xsrfCookieName || defaults . xsrfCookieName ,
534
- xsrfHeaderName = config . xsrfHeaderName || defaults . xsrfHeaderName ,
535
- xsrfToken = isSameDomain ( config . url , $browser . url ( ) ) ?
536
- $browser . cookies ( ) [ xsrfCookieName ] : undefined ;
537
- xsrfHeader [ xsrfHeaderName ] = xsrfToken ;
538
-
539
- var reqTransformFn = config . transformRequest || defaults . transformRequest ,
540
- respTransformFn = config . transformResponse || defaults . transformResponse ,
541
- defHeaders = defaults . headers ,
542
- reqHeaders = extend ( xsrfHeader ,
543
- defHeaders . common , defHeaders [ lowercase ( config . method ) ] , config . headers ) ,
544
- reqData = transformData ( config . data , headersGetter ( reqHeaders ) , reqTransformFn ) ,
545
- promise ;
546
-
547
- // strip content-type if data is undefined
548
- if ( isUndefined ( config . data ) ) {
549
- delete reqHeaders [ 'Content-Type' ] ;
550
- }
655
+ extend ( headers ,
656
+ defaults . headers . common ,
657
+ defaults . headers [ lowercase ( config . method ) ] ,
658
+ requestConfig . headers ) ;
551
659
552
- if ( isUndefined ( config . withCredentials ) && ! isUndefined ( defaults . withCredentials ) ) {
553
- config . withCredentials = defaults . withCredentials ;
660
+ var xsrfValue = isSameDomain ( config . url , $browser . url ( ) )
661
+ ? $browser . cookies ( ) [ config . xsrfCookieName || defaults . xsrfCookieName ]
662
+ : undefined ;
663
+ if ( xsrfValue ) {
664
+ headers [ ( config . xsrfHeaderName || defaults . xsrfHeaderName ) ] = xsrfValue ;
554
665
}
555
666
556
- // send request
557
- promise = sendReq ( config , reqData , reqHeaders ) ;
558
667
668
+ var serverRequest = function ( config ) {
669
+ var reqData = transformData ( config . data , headersGetter ( headers ) , config . transformRequest ) ;
559
670
560
- // transform future response
561
- promise = promise . then ( transformResponse , transformResponse ) ;
671
+ // strip content-type if data is undefined
672
+ if ( isUndefined ( config . data ) ) {
673
+ delete headers [ 'Content-Type' ] ;
674
+ }
675
+
676
+ if ( isUndefined ( config . withCredentials ) && ! isUndefined ( defaults . withCredentials ) ) {
677
+ config . withCredentials = defaults . withCredentials ;
678
+ }
679
+
680
+ // send request
681
+ return sendReq ( config , reqData , headers ) . then ( transformResponse , transformResponse ) ;
682
+ } ;
683
+
684
+ var chain = [ serverRequest , undefined ] ;
685
+ var promise = $q . when ( config ) ;
562
686
563
687
// apply interceptors
564
- forEach ( responseInterceptors , function ( interceptor ) {
565
- promise = interceptor ( promise ) ;
688
+ forEach ( reversedInterceptors , function ( interceptor ) {
689
+ if ( interceptor . request || interceptor . requestError ) {
690
+ chain . unshift ( interceptor . request , interceptor . requestError ) ;
691
+ }
692
+ if ( interceptor . response || interceptor . responseError ) {
693
+ chain . push ( interceptor . response , interceptor . responseError ) ;
694
+ }
566
695
} ) ;
567
696
697
+ while ( chain . length ) {
698
+ var thenFn = chain . shift ( ) ;
699
+ var rejectFn = chain . shift ( ) ;
700
+
701
+ promise = promise . then ( thenFn , rejectFn ) ;
702
+ } ;
703
+
568
704
promise . success = function ( fn ) {
569
705
promise . then ( function ( response ) {
570
706
fn ( response . data , response . status , response . headers , config ) ;
@@ -584,7 +720,7 @@ function $HttpProvider() {
584
720
function transformResponse ( response ) {
585
721
// make a copy since the response must be cacheable
586
722
var resp = extend ( { } , response , {
587
- data : transformData ( response . data , response . headers , respTransformFn )
723
+ data : transformData ( response . data , response . headers , config . transformResponse )
588
724
} ) ;
589
725
return ( isSuccess ( response . status ) )
590
726
? resp
0 commit comments