mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
feat(http): support request/response promise chaining
myApp.factory('myAroundInterceptor', function($rootScope, $timeout) {
return function(configPromise, responsePromise) {
return {
request: configPromise.then(function(config) {
return config
});
response: responsePromise.then(function(response) {
return 'ha!';
}
});
}
myApp.config(function($httpProvider){
$httpProvider.aroundInterceptors.push('myAroundInterceptor');
});
This commit is contained in:
committed by
James deBoer
parent
5c735eb4ab
commit
4ae46814ff
@@ -1018,10 +1018,10 @@ function $CompileProvider($provide) {
|
|||||||
|
|
||||||
|
|
||||||
while(linkQueue.length) {
|
while(linkQueue.length) {
|
||||||
var controller = linkQueue.pop(),
|
var scope = linkQueue.shift(),
|
||||||
linkRootElement = linkQueue.pop(),
|
beforeTemplateLinkNode = linkQueue.shift(),
|
||||||
beforeTemplateLinkNode = linkQueue.pop(),
|
linkRootElement = linkQueue.shift(),
|
||||||
scope = linkQueue.pop(),
|
controller = linkQueue.shift(),
|
||||||
linkNode = compileNode;
|
linkNode = compileNode;
|
||||||
|
|
||||||
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
||||||
|
|||||||
212
src/ng/http.js
212
src/ng/http.js
@@ -155,20 +155,52 @@ function $HttpProvider() {
|
|||||||
xsrfHeaderName: 'X-XSRF-TOKEN'
|
xsrfHeaderName: 'X-XSRF-TOKEN'
|
||||||
};
|
};
|
||||||
|
|
||||||
var providerResponseInterceptors = this.responseInterceptors = [];
|
/**
|
||||||
|
* Are order by request. I.E. they are applied in the same order as
|
||||||
|
* array on request, but revers order on response.
|
||||||
|
*/
|
||||||
|
var interceptorFactories = this.interceptors = [];
|
||||||
|
/**
|
||||||
|
* For historical reasons, response interceptors ordered by the order in which
|
||||||
|
* they are applied to response. (This is in revers to interceptorFactories)
|
||||||
|
*/
|
||||||
|
var responseInterceptorFactories = this.responseInterceptors = [];
|
||||||
|
|
||||||
this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
|
this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
|
||||||
function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
|
function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
|
||||||
|
|
||||||
var defaultCache = $cacheFactory('$http'),
|
var defaultCache = $cacheFactory('$http');
|
||||||
responseInterceptors = [];
|
|
||||||
|
|
||||||
forEach(providerResponseInterceptors, function(interceptor) {
|
/**
|
||||||
responseInterceptors.push(
|
* Interceptors stored in reverse order. Inner interceptors before outer interceptors.
|
||||||
isString(interceptor)
|
* The reversal is needed so that we can build up the interception chain around the
|
||||||
? $injector.get(interceptor)
|
* server request.
|
||||||
: $injector.invoke(interceptor)
|
*/
|
||||||
);
|
var reversedInterceptors = [];
|
||||||
|
|
||||||
|
forEach(interceptorFactories, function(interceptorFactory) {
|
||||||
|
reversedInterceptors.unshift(isString(interceptorFactory)
|
||||||
|
? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
|
||||||
|
});
|
||||||
|
|
||||||
|
forEach(responseInterceptorFactories, function(interceptorFactory, index) {
|
||||||
|
var responseFn = isString(interceptorFactory)
|
||||||
|
? $injector.get(interceptorFactory)
|
||||||
|
: $injector.invoke(interceptorFactory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response interceptors go before "around" interceptors (no real reason, just
|
||||||
|
* had to pick one.) But they are already revesed, so we can't use unshift, hence
|
||||||
|
* the splice.
|
||||||
|
*/
|
||||||
|
reversedInterceptors.splice(index, 0, {
|
||||||
|
response: function(response) {
|
||||||
|
return responseFn($q.when(response));
|
||||||
|
},
|
||||||
|
responseError: function(response) {
|
||||||
|
return responseFn($q.reject(response));
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -310,7 +342,90 @@ function $HttpProvider() {
|
|||||||
* To skip it, set configuration property `cache` to `false`.
|
* To skip it, set configuration property `cache` to `false`.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* # Response interceptors
|
* # Interceptors
|
||||||
|
*
|
||||||
|
* Before you start creating interceptors, be sure to understand the
|
||||||
|
* {@link ng.$q $q and deferred/promise APIs}.
|
||||||
|
*
|
||||||
|
* For purposes of global error handling, authentication or any kind of synchronous or
|
||||||
|
* asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
|
||||||
|
* able to intercept requests before they are handed to the server and
|
||||||
|
* responses before they are handed over to the application code that
|
||||||
|
* initiated these requests. The interceptors leverage the {@link ng.$q
|
||||||
|
* promise APIs} to fulfil this need for both synchronous and asynchronous pre-processing.
|
||||||
|
*
|
||||||
|
* The interceptors are service factories that are registered with the $httpProvider by
|
||||||
|
* adding them to the `$httpProvider.interceptors` array. The factory is called and
|
||||||
|
* injected with dependencies (if specified) and returns the interceptor.
|
||||||
|
*
|
||||||
|
* There are two kinds of interceptors (and two kinds of rejection interceptors):
|
||||||
|
*
|
||||||
|
* * `request`: interceptors get called with http `config` object. The function is free to modify
|
||||||
|
* the `config` or create a new one. The function needs to return the `config` directly or as a
|
||||||
|
* promise.
|
||||||
|
* * `requestError`: interceptor gets called when a previous interceptor threw an error or resolved
|
||||||
|
* with a rejection.
|
||||||
|
* * `response`: interceptors get called with http `response` object. The function is free to modify
|
||||||
|
* the `response` or create a new one. The function needs to return the `response` directly or as a
|
||||||
|
* promise.
|
||||||
|
* * `responseError`: interceptor gets called when a previous interceptor threw an error or resolved
|
||||||
|
* with a rejection.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* // register the interceptor as a service
|
||||||
|
* $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
|
||||||
|
* return {
|
||||||
|
* // optional method
|
||||||
|
* 'request': function(config) {
|
||||||
|
* // do something on success
|
||||||
|
* return config || $q.when(config);
|
||||||
|
* },
|
||||||
|
*
|
||||||
|
* // optional method
|
||||||
|
* 'requestError': function(rejection) {
|
||||||
|
* // do something on error
|
||||||
|
* if (canRecover(rejection)) {
|
||||||
|
* return responseOrNewPromise
|
||||||
|
* }
|
||||||
|
* return $q.reject(rejection);
|
||||||
|
* },
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* // optional method
|
||||||
|
* 'response': function(response) {
|
||||||
|
* // do something on success
|
||||||
|
* return response || $q.when(response);
|
||||||
|
* },
|
||||||
|
*
|
||||||
|
* // optional method
|
||||||
|
* 'responseError': function(rejection) {
|
||||||
|
* // do something on error
|
||||||
|
* if (canRecover(rejection)) {
|
||||||
|
* return responseOrNewPromise
|
||||||
|
* }
|
||||||
|
* return $q.reject(rejection);
|
||||||
|
* };
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* $httpProvider.interceptors.push('myHttpInterceptor');
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* // register the interceptor via an anonymous factory
|
||||||
|
* $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
|
||||||
|
* return {
|
||||||
|
* 'request': function(config) {
|
||||||
|
* // same as above
|
||||||
|
* },
|
||||||
|
* 'response': function(response) {
|
||||||
|
* // same as above
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* # Response interceptors (DEPRECATED)
|
||||||
*
|
*
|
||||||
* Before you start creating interceptors, be sure to understand the
|
* Before you start creating interceptors, be sure to understand the
|
||||||
* {@link ng.$q $q and deferred/promise APIs}.
|
* {@link ng.$q $q and deferred/promise APIs}.
|
||||||
@@ -526,45 +641,66 @@ function $HttpProvider() {
|
|||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
*/
|
*/
|
||||||
function $http(config) {
|
function $http(requestConfig) {
|
||||||
|
var config = {
|
||||||
|
transformRequest: defaults.transformRequest,
|
||||||
|
transformResponse: defaults.transformResponse
|
||||||
|
};
|
||||||
|
var headers = {};
|
||||||
|
|
||||||
|
extend(config, requestConfig);
|
||||||
|
config.headers = headers;
|
||||||
config.method = uppercase(config.method);
|
config.method = uppercase(config.method);
|
||||||
|
|
||||||
var xsrfHeader = {},
|
extend(headers,
|
||||||
xsrfCookieName = config.xsrfCookieName || defaults.xsrfCookieName,
|
defaults.headers.common,
|
||||||
xsrfHeaderName = config.xsrfHeaderName || defaults.xsrfHeaderName,
|
defaults.headers[lowercase(config.method)],
|
||||||
xsrfToken = isSameDomain(config.url, $browser.url()) ?
|
requestConfig.headers);
|
||||||
$browser.cookies()[xsrfCookieName] : undefined;
|
|
||||||
xsrfHeader[xsrfHeaderName] = xsrfToken;
|
|
||||||
|
|
||||||
var reqTransformFn = config.transformRequest || defaults.transformRequest,
|
var xsrfValue = isSameDomain(config.url, $browser.url())
|
||||||
respTransformFn = config.transformResponse || defaults.transformResponse,
|
? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
|
||||||
defHeaders = defaults.headers,
|
: undefined;
|
||||||
reqHeaders = extend(xsrfHeader,
|
if (xsrfValue) {
|
||||||
defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
|
headers[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
|
||||||
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
|
|
||||||
promise;
|
|
||||||
|
|
||||||
// strip content-type if data is undefined
|
|
||||||
if (isUndefined(config.data)) {
|
|
||||||
delete reqHeaders['Content-Type'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
|
|
||||||
config.withCredentials = defaults.withCredentials;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send request
|
var serverRequest = function(config) {
|
||||||
promise = sendReq(config, reqData, reqHeaders);
|
var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
|
||||||
|
|
||||||
|
// strip content-type if data is undefined
|
||||||
|
if (isUndefined(config.data)) {
|
||||||
|
delete headers['Content-Type'];
|
||||||
|
}
|
||||||
|
|
||||||
// transform future response
|
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
|
||||||
promise = promise.then(transformResponse, transformResponse);
|
config.withCredentials = defaults.withCredentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send request
|
||||||
|
return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
|
||||||
|
};
|
||||||
|
|
||||||
|
var chain = [serverRequest, undefined];
|
||||||
|
var promise = $q.when(config);
|
||||||
|
|
||||||
// apply interceptors
|
// apply interceptors
|
||||||
forEach(responseInterceptors, function(interceptor) {
|
forEach(reversedInterceptors, function(interceptor) {
|
||||||
promise = interceptor(promise);
|
if (interceptor.request || interceptor.requestError) {
|
||||||
|
chain.unshift(interceptor.request, interceptor.requestError);
|
||||||
|
}
|
||||||
|
if (interceptor.response || interceptor.responseError) {
|
||||||
|
chain.push(interceptor.response, interceptor.responseError);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
while(chain.length) {
|
||||||
|
var thenFn = chain.shift();
|
||||||
|
var rejectFn = chain.shift();
|
||||||
|
|
||||||
|
promise = promise.then(thenFn, rejectFn);
|
||||||
|
};
|
||||||
|
|
||||||
promise.success = function(fn) {
|
promise.success = function(fn) {
|
||||||
promise.then(function(response) {
|
promise.then(function(response) {
|
||||||
fn(response.data, response.status, response.headers, config);
|
fn(response.data, response.status, response.headers, config);
|
||||||
@@ -584,7 +720,7 @@ function $HttpProvider() {
|
|||||||
function transformResponse(response) {
|
function transformResponse(response) {
|
||||||
// make a copy since the response must be cacheable
|
// make a copy since the response must be cacheable
|
||||||
var resp = extend({}, response, {
|
var resp = extend({}, response, {
|
||||||
data: transformData(response.data, response.headers, respTransformFn)
|
data: transformData(response.data, response.headers, config.transformResponse)
|
||||||
});
|
});
|
||||||
return (isSuccess(response.status))
|
return (isSuccess(response.status))
|
||||||
? resp
|
? resp
|
||||||
|
|||||||
8
src/ngMock/angular-mocks.js
vendored
8
src/ngMock/angular-mocks.js
vendored
@@ -826,7 +826,7 @@ angular.mock.dump = function(object) {
|
|||||||
</pre>
|
</pre>
|
||||||
*/
|
*/
|
||||||
angular.mock.$HttpBackendProvider = function() {
|
angular.mock.$HttpBackendProvider = function() {
|
||||||
this.$get = [createHttpBackendMock];
|
this.$get = ['$rootScope', createHttpBackendMock];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -843,7 +843,7 @@ angular.mock.$HttpBackendProvider = function() {
|
|||||||
* @param {Object=} $browser Auto-flushing enabled if specified
|
* @param {Object=} $browser Auto-flushing enabled if specified
|
||||||
* @return {Object} Instance of $httpBackend mock
|
* @return {Object} Instance of $httpBackend mock
|
||||||
*/
|
*/
|
||||||
function createHttpBackendMock($delegate, $browser) {
|
function createHttpBackendMock($rootScope, $delegate, $browser) {
|
||||||
var definitions = [],
|
var definitions = [],
|
||||||
expectations = [],
|
expectations = [],
|
||||||
responses = [],
|
responses = [],
|
||||||
@@ -1173,6 +1173,7 @@ function createHttpBackendMock($delegate, $browser) {
|
|||||||
* is called an exception is thrown (as this typically a sign of programming error).
|
* is called an exception is thrown (as this typically a sign of programming error).
|
||||||
*/
|
*/
|
||||||
$httpBackend.flush = function(count) {
|
$httpBackend.flush = function(count) {
|
||||||
|
$rootScope.$digest();
|
||||||
if (!responses.length) throw Error('No pending request to flush !');
|
if (!responses.length) throw Error('No pending request to flush !');
|
||||||
|
|
||||||
if (angular.isDefined(count)) {
|
if (angular.isDefined(count)) {
|
||||||
@@ -1205,6 +1206,7 @@ function createHttpBackendMock($delegate, $browser) {
|
|||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
$httpBackend.verifyNoOutstandingExpectation = function() {
|
$httpBackend.verifyNoOutstandingExpectation = function() {
|
||||||
|
$rootScope.$digest();
|
||||||
if (expectations.length) {
|
if (expectations.length) {
|
||||||
throw Error('Unsatisfied requests: ' + expectations.join(', '));
|
throw Error('Unsatisfied requests: ' + expectations.join(', '));
|
||||||
}
|
}
|
||||||
@@ -1606,7 +1608,7 @@ angular.module('ngMockE2E', ['ng']).config(function($provide) {
|
|||||||
* control how a matched request is handled.
|
* control how a matched request is handled.
|
||||||
*/
|
*/
|
||||||
angular.mock.e2e = {};
|
angular.mock.e2e = {};
|
||||||
angular.mock.e2e.$httpBackendDecorator = ['$delegate', '$browser', createHttpBackendMock];
|
angular.mock.e2e.$httpBackendDecorator = ['$rootScope', '$delegate', '$browser', createHttpBackendMock];
|
||||||
|
|
||||||
|
|
||||||
angular.mock.clearDataCache = function() {
|
angular.mock.clearDataCache = function() {
|
||||||
|
|||||||
@@ -178,25 +178,23 @@ describe('ngInclude', function() {
|
|||||||
it('should discard pending xhr callbacks if a new template is requested before the current ' +
|
it('should discard pending xhr callbacks if a new template is requested before the current ' +
|
||||||
'finished loading', inject(function($rootScope, $compile, $httpBackend) {
|
'finished loading', inject(function($rootScope, $compile, $httpBackend) {
|
||||||
element = jqLite("<ng:include src='templateUrl'></ng:include>");
|
element = jqLite("<ng:include src='templateUrl'></ng:include>");
|
||||||
var log = [];
|
var log = {};
|
||||||
|
|
||||||
$rootScope.templateUrl = 'myUrl1';
|
$rootScope.templateUrl = 'myUrl1';
|
||||||
$rootScope.logger = function(msg) {
|
$rootScope.logger = function(msg) {
|
||||||
log.push(msg);
|
log[msg] = true;
|
||||||
}
|
}
|
||||||
$compile(element)($rootScope);
|
$compile(element)($rootScope);
|
||||||
expect(log.join('; ')).toEqual('');
|
expect(log).toEqual({});
|
||||||
|
|
||||||
$httpBackend.expect('GET', 'myUrl1').respond('<div>{{logger("url1")}}</div>');
|
$httpBackend.expect('GET', 'myUrl1').respond('<div>{{logger("url1")}}</div>');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(log.join('; ')).toEqual('');
|
expect(log).toEqual({});
|
||||||
$rootScope.templateUrl = 'myUrl2';
|
$rootScope.templateUrl = 'myUrl2';
|
||||||
$httpBackend.expect('GET', 'myUrl2').respond('<div>{{logger("url2")}}</div>');
|
$httpBackend.expect('GET', 'myUrl2').respond('<div>{{logger("url2")}}</div>');
|
||||||
$rootScope.$digest();
|
|
||||||
$httpBackend.flush(); // now that we have two requests pending, flush!
|
$httpBackend.flush(); // now that we have two requests pending, flush!
|
||||||
|
|
||||||
expect(log.join('; ')).toEqual('url2; url2'); // it's here twice because we go through at
|
expect(log).toEqual({ url2 : true });
|
||||||
// least two digest cycles
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('$http', function() {
|
|||||||
$exceptionHandlerProvider.mode('log');
|
$exceptionHandlerProvider.mode('log');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
afterEach(inject(function($exceptionHandler, $httpBackend) {
|
afterEach(inject(function($exceptionHandler, $httpBackend, $rootScope) {
|
||||||
forEach($exceptionHandler.errors, function(e) {
|
forEach($exceptionHandler.errors, function(e) {
|
||||||
dump('Unhandled exception: ', e)
|
dump('Unhandled exception: ', e)
|
||||||
});
|
});
|
||||||
@@ -21,13 +21,150 @@ describe('$http', function() {
|
|||||||
throw 'Unhandled exceptions trapped in $exceptionHandler!';
|
throw 'Unhandled exceptions trapped in $exceptionHandler!';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rootScope.$digest();
|
||||||
$httpBackend.verifyNoOutstandingExpectation();
|
$httpBackend.verifyNoOutstandingExpectation();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
describe('$httpProvider', function() {
|
describe('$httpProvider', function() {
|
||||||
|
|
||||||
describe('interceptors', function() {
|
describe('interceptors', function() {
|
||||||
|
it('should accept injected rejected response interceptor', function() {
|
||||||
|
var wasCalled = false;
|
||||||
|
module(function($httpProvider, $provide) {
|
||||||
|
$httpProvider.responseInterceptors.push('injectedInterceptor');
|
||||||
|
$provide.factory('injectedInterceptor', ['$q', function($q) {
|
||||||
|
return function(promise) {
|
||||||
|
return promise.then(null, function authInterceptor(response) {
|
||||||
|
wasCalled = true;
|
||||||
|
expect(response.status).toEqual(401);
|
||||||
|
return $q.reject(response);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend) {
|
||||||
|
$httpBackend.expect('GET', '/url').respond(401);
|
||||||
|
$http({method: 'GET', url: '/url'});
|
||||||
|
$httpBackend.flush();
|
||||||
|
expect(wasCalled).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should chain request, requestReject, response and responseReject interceptors', function() {
|
||||||
|
module(function($httpProvider) {
|
||||||
|
var savedConfig, savedResponse;
|
||||||
|
$httpProvider.interceptors.push(function($q) {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
config.url += '/1';
|
||||||
|
savedConfig = config;
|
||||||
|
return $q.reject('/2');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.interceptors.push(function($q) {
|
||||||
|
return {
|
||||||
|
requestError: function(error) {
|
||||||
|
savedConfig.url += error;
|
||||||
|
return $q.when(savedConfig);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.interceptors.push(function() {
|
||||||
|
return {
|
||||||
|
responseError: function(rejection) {
|
||||||
|
savedResponse.data += rejection;
|
||||||
|
return savedResponse;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.interceptors.push(function($q) {
|
||||||
|
return {
|
||||||
|
response: function(response) {
|
||||||
|
response.data += ':1';
|
||||||
|
savedResponse = response
|
||||||
|
return $q.reject(':2');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend, $rootScope) {
|
||||||
|
var response;
|
||||||
|
$httpBackend.expect('GET', '/url/1/2').respond('response');
|
||||||
|
$http({method: 'GET', url: '/url'}).then(function(r) {
|
||||||
|
response = r;
|
||||||
|
});
|
||||||
|
$rootScope.$apply();
|
||||||
|
$httpBackend.flush();
|
||||||
|
expect(response.data).toEqual('response:1:2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should verify order of execution', function() {
|
||||||
|
module(function($httpProvider) {
|
||||||
|
$httpProvider.interceptors.push(function($q) {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
config.url += '/outer';
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
response: function(response) {
|
||||||
|
response.data = '{' + response.data + '} outer';
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.interceptors.push(function($q) {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
config.url += '/inner';
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
response: function(response) {
|
||||||
|
response.data = '{' + response.data + '} inner';
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.responseInterceptors.push(function($q) {
|
||||||
|
return function(promise) {
|
||||||
|
var defer = $q.defer();
|
||||||
|
|
||||||
|
promise.then(function(response) {
|
||||||
|
response.data = '[' + response.data + '] legacy-1';
|
||||||
|
defer.resolve(response);
|
||||||
|
});
|
||||||
|
return defer.promise;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.responseInterceptors.push(function($q) {
|
||||||
|
return function(promise) {
|
||||||
|
var defer = $q.defer();
|
||||||
|
|
||||||
|
promise.then(function(response) {
|
||||||
|
response.data = '[' + response.data + '] legacy-2';
|
||||||
|
defer.resolve(response);
|
||||||
|
});
|
||||||
|
return defer.promise;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend) {
|
||||||
|
var response;
|
||||||
|
$httpBackend.expect('GET', '/url/outer/inner').respond('response');
|
||||||
|
$http({method: 'GET', url: '/url'}).then(function(r) {
|
||||||
|
response = r;
|
||||||
|
});
|
||||||
|
$httpBackend.flush();
|
||||||
|
expect(response.data).toEqual('{{[[response] legacy-1] legacy-2} inner} outer');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('response interceptors', function() {
|
||||||
|
|
||||||
it('should default to an empty array', module(function($httpProvider) {
|
it('should default to an empty array', module(function($httpProvider) {
|
||||||
expect($httpProvider.responseInterceptors).toEqual([]);
|
expect($httpProvider.responseInterceptors).toEqual([]);
|
||||||
@@ -44,7 +181,7 @@ describe('$http', function() {
|
|||||||
data: response.data + '?',
|
data: response.data + '?',
|
||||||
status: 209,
|
status: 209,
|
||||||
headers: response.headers,
|
headers: response.headers,
|
||||||
config: response.config
|
request: response.config
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
});
|
});
|
||||||
@@ -100,6 +237,136 @@ describe('$http', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('request interceptors', function() {
|
||||||
|
it('should pass request config as a promise', function() {
|
||||||
|
var run = false;
|
||||||
|
module(function($httpProvider) {
|
||||||
|
$httpProvider.interceptors.push(function() {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
expect(config.url).toEqual('/url');
|
||||||
|
expect(config.data).toEqual({one: "two"});
|
||||||
|
expect(config.headers.foo).toEqual('bar');
|
||||||
|
run = true;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend, $rootScope) {
|
||||||
|
$httpBackend.expect('POST', '/url').respond('');
|
||||||
|
$http({method: 'POST', url: '/url', data: {one: 'two'}, headers: {foo: 'bar'}});
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(run).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow manipulation of request', function() {
|
||||||
|
module(function($httpProvider) {
|
||||||
|
$httpProvider.interceptors.push(function() {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
config.url = '/intercepted';
|
||||||
|
config.headers.foo = 'intercepted';
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend, $rootScope) {
|
||||||
|
$httpBackend.expect('GET', '/intercepted', null, function (headers) {
|
||||||
|
return headers.foo === 'intercepted';
|
||||||
|
}).respond('');
|
||||||
|
$http.get('/url');
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject the http promise if an interceptor fails', function() {
|
||||||
|
var reason = new Error('interceptor failed');
|
||||||
|
module(function($httpProvider) {
|
||||||
|
$httpProvider.interceptors.push(function($q) {
|
||||||
|
return {
|
||||||
|
request: function(promise) {
|
||||||
|
return $q.reject(reason);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend, $rootScope) {
|
||||||
|
var success = jasmine.createSpy(), error = jasmine.createSpy();
|
||||||
|
$http.get('/url').then(success, error);
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(success).not.toHaveBeenCalled();
|
||||||
|
expect(error).toHaveBeenCalledWith(reason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not manipulate the passed-in config', function() {
|
||||||
|
module(function($httpProvider) {
|
||||||
|
$httpProvider.interceptors.push(function() {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
config.url = '/intercepted';
|
||||||
|
config.headers.foo = 'intercepted';
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend, $rootScope) {
|
||||||
|
var config = { method: 'get', url: '/url', headers: { foo: 'bar'} };
|
||||||
|
$httpBackend.expect('GET', '/intercepted').respond('');
|
||||||
|
$http.get('/url');
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(config.method).toEqual('get');
|
||||||
|
expect(config.url).toEqual('/url');
|
||||||
|
expect(config.headers.foo).toEqual('bar')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support interceptors defined as services', function() {
|
||||||
|
module(function($provide, $httpProvider) {
|
||||||
|
$provide.factory('myInterceptor', function() {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
config.url = '/intercepted';
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.interceptors.push('myInterceptor');
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend, $rootScope) {
|
||||||
|
$httpBackend.expect('POST', '/intercepted').respond('');
|
||||||
|
$http.post('/url');
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support complex interceptors based on promises', function() {
|
||||||
|
module(function($provide, $httpProvider) {
|
||||||
|
$provide.factory('myInterceptor', function($q, $rootScope) {
|
||||||
|
return {
|
||||||
|
request: function(config) {
|
||||||
|
return $q.when('/intercepted').then(function(intercepted) {
|
||||||
|
config.url = intercepted;
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
$httpProvider.interceptors.push('myInterceptor');
|
||||||
|
});
|
||||||
|
inject(function($http, $httpBackend, $rootScope) {
|
||||||
|
$httpBackend.expect('POST', '/intercepted').respond('');
|
||||||
|
$http.post('/two');
|
||||||
|
$rootScope.$apply();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -938,7 +1205,7 @@ describe('$http', function() {
|
|||||||
$http({method: 'GET', url: '/url'}); // Notice no cache given in config.
|
$http({method: 'GET', url: '/url'}); // Notice no cache given in config.
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
// Second should be served from cache, without sending request to server.
|
// Second should be served from cache, without sending request to server.
|
||||||
$http({method: 'get', url: '/url'}).success(callback);
|
$http({method: 'get', url: '/url'}).success(callback);
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
@@ -1004,6 +1271,7 @@ describe('$http', function() {
|
|||||||
expect($http.pendingRequests.length).toBe(0);
|
expect($http.pendingRequests.length).toBe(0);
|
||||||
|
|
||||||
$http({method: 'get', url: '/some'});
|
$http({method: 'get', url: '/some'});
|
||||||
|
$rootScope.$digest();
|
||||||
expect($http.pendingRequests.length).toBe(1);
|
expect($http.pendingRequests.length).toBe(1);
|
||||||
|
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
@@ -1016,13 +1284,16 @@ describe('$http', function() {
|
|||||||
|
|
||||||
$http({method: 'get', url: '/cached', cache: true});
|
$http({method: 'get', url: '/cached', cache: true});
|
||||||
$http({method: 'get', url: '/cached', cache: true});
|
$http({method: 'get', url: '/cached', cache: true});
|
||||||
|
$rootScope.$digest();
|
||||||
expect($http.pendingRequests.length).toBe(2);
|
expect($http.pendingRequests.length).toBe(2);
|
||||||
|
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
expect($http.pendingRequests.length).toBe(0);
|
expect($http.pendingRequests.length).toBe(0);
|
||||||
|
|
||||||
$http({method: 'get', url: '/cached', cache: true});
|
$http({method: 'get', url: '/cached', cache: true});
|
||||||
expect($http.pendingRequests.length).toBe(1);
|
spyOn($http.pendingRequests, 'push').andCallThrough();
|
||||||
|
$rootScope.$digest();
|
||||||
|
expect($http.pendingRequests.push).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
$rootScope.$apply();
|
$rootScope.$apply();
|
||||||
expect($http.pendingRequests.length).toBe(0);
|
expect($http.pendingRequests.length).toBe(0);
|
||||||
@@ -1035,6 +1306,7 @@ describe('$http', function() {
|
|||||||
expect($http.pendingRequests.length).toBe(0);
|
expect($http.pendingRequests.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$rootScope.$digest();
|
||||||
expect($http.pendingRequests.length).toBe(1);
|
expect($http.pendingRequests.length).toBe(1);
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
});
|
});
|
||||||
@@ -1071,10 +1343,11 @@ describe('$http', function() {
|
|||||||
$provide.value('$httpBackend', $httpBackend);
|
$provide.value('$httpBackend', $httpBackend);
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function($http) {
|
inject(function($http, $rootScope) {
|
||||||
$http({
|
$http({
|
||||||
method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true, responseType: 'json'
|
method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true, responseType: 'json'
|
||||||
});
|
});
|
||||||
|
$rootScope.$digest();
|
||||||
expect($httpBackend).toHaveBeenCalledOnce();
|
expect($httpBackend).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1093,11 +1366,12 @@ describe('$http', function() {
|
|||||||
$provide.value('$httpBackend', $httpBackend);
|
$provide.value('$httpBackend', $httpBackend);
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function($http) {
|
inject(function($http, $rootScope) {
|
||||||
$http.defaults.withCredentials = true;
|
$http.defaults.withCredentials = true;
|
||||||
$http({
|
$http({
|
||||||
method: 'GET', url: 'some.html', timeout: 12345, responseType: 'json'
|
method: 'GET', url: 'some.html', timeout: 12345, responseType: 'json'
|
||||||
});
|
});
|
||||||
|
$rootScope.$digest();
|
||||||
expect($httpBackend).toHaveBeenCalledOnce();
|
expect($httpBackend).toHaveBeenCalledOnce();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -468,12 +468,9 @@ describe("resource", function() {
|
|||||||
|
|
||||||
var response = callback.mostRecentCall.args[0];
|
var response = callback.mostRecentCall.args[0];
|
||||||
|
|
||||||
expect(response).toEqualData({
|
expect(response.data).toEqual({id: 123, number: '9876'});
|
||||||
data: {id: 123, number: '9876'},
|
expect(response.status).toEqual(200);
|
||||||
status: 200,
|
expect(response.resource).toEqualData({id: 123, number: '9876', $resolved: true});
|
||||||
config: {method: 'GET', data: undefined, url: '/CreditCard/123'},
|
|
||||||
resource: {id: 123, number: '9876', $resolved: true}
|
|
||||||
});
|
|
||||||
expect(typeof response.resource.$save).toBe('function');
|
expect(typeof response.resource.$save).toBe('function');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -516,11 +513,8 @@ describe("resource", function() {
|
|||||||
|
|
||||||
var response = callback.mostRecentCall.args[0];
|
var response = callback.mostRecentCall.args[0];
|
||||||
|
|
||||||
expect(response).toEqualData({
|
expect(response.data).toEqual('resource not found');
|
||||||
data : 'resource not found',
|
expect(response.status).toEqual(404);
|
||||||
status : 404,
|
|
||||||
config : { method : 'GET', data : undefined, url : '/CreditCard/123' }
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -564,12 +558,9 @@ describe("resource", function() {
|
|||||||
|
|
||||||
var response = callback.mostRecentCall.args[0];
|
var response = callback.mostRecentCall.args[0];
|
||||||
|
|
||||||
expect(response).toEqualData({
|
expect(response.data).toEqual([{id: 1}, {id :2}]);
|
||||||
data: [{id: 1}, {id :2}],
|
expect(response.status).toEqual(200);
|
||||||
status: 200,
|
expect(response.resource).toEqualData([ { id : 1 }, { id : 2 } ]);
|
||||||
config: {method: 'GET', data: undefined, url: '/CreditCard', params: {key: 'value'}},
|
|
||||||
resource: [ { id : 1 }, { id : 2 } ]
|
|
||||||
});
|
|
||||||
expect(typeof response.resource[0].$save).toBe('function');
|
expect(typeof response.resource[0].$save).toBe('function');
|
||||||
expect(typeof response.resource[1].$save).toBe('function');
|
expect(typeof response.resource[1].$save).toBe('function');
|
||||||
});
|
});
|
||||||
@@ -613,11 +604,8 @@ describe("resource", function() {
|
|||||||
|
|
||||||
var response = callback.mostRecentCall.args[0];
|
var response = callback.mostRecentCall.args[0];
|
||||||
|
|
||||||
expect(response).toEqualData({
|
expect(response.data).toEqual('resource not found');
|
||||||
data : 'resource not found',
|
expect(response.status).toEqual(404);
|
||||||
status : 404,
|
|
||||||
config : { method : 'GET', data : undefined, url : '/CreditCard', params: {key: 'value'}}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user