@@ -155,20 +155,52 @@ function $HttpProvider() {
155155 xsrfHeaderName : 'X-XSRF-TOKEN'
156156 } ;
157157
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 = [ ] ;
159168
160169 this . $get = [ '$httpBackend' , '$browser' , '$cacheFactory' , '$rootScope' , '$q' , '$injector' ,
161170 function ( $httpBackend , $browser , $cacheFactory , $rootScope , $q , $injector ) {
162171
163- var defaultCache = $cacheFactory ( '$http' ) ,
164- responseInterceptors = [ ] ;
172+ var defaultCache = $cacheFactory ( '$http' ) ;
165173
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+ } ) ;
172204 } ) ;
173205
174206
@@ -310,7 +342,90 @@ function $HttpProvider() {
310342 * To skip it, set configuration property `cache` to `false`.
311343 *
312344 *
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)
314429 *
315430 * Before you start creating interceptors, be sure to understand the
316431 * {@link ng.$q $q and deferred/promise APIs }.
@@ -526,45 +641,66 @@ function $HttpProvider() {
526641 </file>
527642 </example>
528643 */
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 ;
530653 config . method = uppercase ( config . method ) ;
531654
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 ) ;
551659
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 ;
554665 }
555666
556- // send request
557- promise = sendReq ( config , reqData , reqHeaders ) ;
558667
668+ var serverRequest = function ( config ) {
669+ var reqData = transformData ( config . data , headersGetter ( headers ) , config . transformRequest ) ;
559670
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 ) ;
562686
563687 // 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+ }
566695 } ) ;
567696
697+ while ( chain . length ) {
698+ var thenFn = chain . shift ( ) ;
699+ var rejectFn = chain . shift ( ) ;
700+
701+ promise = promise . then ( thenFn , rejectFn ) ;
702+ } ;
703+
568704 promise . success = function ( fn ) {
569705 promise . then ( function ( response ) {
570706 fn ( response . data , response . status , response . headers , config ) ;
@@ -584,7 +720,7 @@ function $HttpProvider() {
584720 function transformResponse ( response ) {
585721 // make a copy since the response must be cacheable
586722 var resp = extend ( { } , response , {
587- data : transformData ( response . data , response . headers , respTransformFn )
723+ data : transformData ( response . data , response . headers , config . transformResponse )
588724 } ) ;
589725 return ( isSuccess ( response . status ) )
590726 ? resp
0 commit comments