mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-18 12:08:17 +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
@@ -178,25 +178,23 @@ describe('ngInclude', function() {
|
||||
it('should discard pending xhr callbacks if a new template is requested before the current ' +
|
||||
'finished loading', inject(function($rootScope, $compile, $httpBackend) {
|
||||
element = jqLite("<ng:include src='templateUrl'></ng:include>");
|
||||
var log = [];
|
||||
var log = {};
|
||||
|
||||
$rootScope.templateUrl = 'myUrl1';
|
||||
$rootScope.logger = function(msg) {
|
||||
log.push(msg);
|
||||
log[msg] = true;
|
||||
}
|
||||
$compile(element)($rootScope);
|
||||
expect(log.join('; ')).toEqual('');
|
||||
expect(log).toEqual({});
|
||||
|
||||
$httpBackend.expect('GET', 'myUrl1').respond('<div>{{logger("url1")}}</div>');
|
||||
$rootScope.$digest();
|
||||
expect(log.join('; ')).toEqual('');
|
||||
expect(log).toEqual({});
|
||||
$rootScope.templateUrl = 'myUrl2';
|
||||
$httpBackend.expect('GET', 'myUrl2').respond('<div>{{logger("url2")}}</div>');
|
||||
$rootScope.$digest();
|
||||
$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
|
||||
// least two digest cycles
|
||||
expect(log).toEqual({ url2 : true });
|
||||
}));
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('$http', function() {
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
}));
|
||||
|
||||
afterEach(inject(function($exceptionHandler, $httpBackend) {
|
||||
afterEach(inject(function($exceptionHandler, $httpBackend, $rootScope) {
|
||||
forEach($exceptionHandler.errors, function(e) {
|
||||
dump('Unhandled exception: ', e)
|
||||
});
|
||||
@@ -21,13 +21,150 @@ describe('$http', function() {
|
||||
throw 'Unhandled exceptions trapped in $exceptionHandler!';
|
||||
}
|
||||
|
||||
$rootScope.$digest();
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
}));
|
||||
|
||||
|
||||
describe('$httpProvider', 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) {
|
||||
expect($httpProvider.responseInterceptors).toEqual([]);
|
||||
@@ -44,7 +181,7 @@ describe('$http', function() {
|
||||
data: response.data + '?',
|
||||
status: 209,
|
||||
headers: response.headers,
|
||||
config: response.config
|
||||
request: response.config
|
||||
});
|
||||
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.
|
||||
$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);
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -1004,6 +1271,7 @@ describe('$http', function() {
|
||||
expect($http.pendingRequests.length).toBe(0);
|
||||
|
||||
$http({method: 'get', url: '/some'});
|
||||
$rootScope.$digest();
|
||||
expect($http.pendingRequests.length).toBe(1);
|
||||
|
||||
$httpBackend.flush();
|
||||
@@ -1016,13 +1284,16 @@ describe('$http', function() {
|
||||
|
||||
$http({method: 'get', url: '/cached', cache: true});
|
||||
$http({method: 'get', url: '/cached', cache: true});
|
||||
$rootScope.$digest();
|
||||
expect($http.pendingRequests.length).toBe(2);
|
||||
|
||||
$httpBackend.flush();
|
||||
expect($http.pendingRequests.length).toBe(0);
|
||||
|
||||
$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();
|
||||
expect($http.pendingRequests.length).toBe(0);
|
||||
@@ -1035,6 +1306,7 @@ describe('$http', function() {
|
||||
expect($http.pendingRequests.length).toBe(0);
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
expect($http.pendingRequests.length).toBe(1);
|
||||
$httpBackend.flush();
|
||||
});
|
||||
@@ -1071,10 +1343,11 @@ describe('$http', function() {
|
||||
$provide.value('$httpBackend', $httpBackend);
|
||||
});
|
||||
|
||||
inject(function($http) {
|
||||
inject(function($http, $rootScope) {
|
||||
$http({
|
||||
method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true, responseType: 'json'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
expect($httpBackend).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
@@ -1093,11 +1366,12 @@ describe('$http', function() {
|
||||
$provide.value('$httpBackend', $httpBackend);
|
||||
});
|
||||
|
||||
inject(function($http) {
|
||||
inject(function($http, $rootScope) {
|
||||
$http.defaults.withCredentials = true;
|
||||
$http({
|
||||
method: 'GET', url: 'some.html', timeout: 12345, responseType: 'json'
|
||||
});
|
||||
$rootScope.$digest();
|
||||
expect($httpBackend).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
|
||||
@@ -468,12 +468,9 @@ describe("resource", function() {
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data: {id: 123, number: '9876'},
|
||||
status: 200,
|
||||
config: {method: 'GET', data: undefined, url: '/CreditCard/123'},
|
||||
resource: {id: 123, number: '9876', $resolved: true}
|
||||
});
|
||||
expect(response.data).toEqual({id: 123, number: '9876'});
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.resource).toEqualData({id: 123, number: '9876', $resolved: true});
|
||||
expect(typeof response.resource.$save).toBe('function');
|
||||
});
|
||||
|
||||
@@ -516,11 +513,8 @@ describe("resource", function() {
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data : 'resource not found',
|
||||
status : 404,
|
||||
config : { method : 'GET', data : undefined, url : '/CreditCard/123' }
|
||||
});
|
||||
expect(response.data).toEqual('resource not found');
|
||||
expect(response.status).toEqual(404);
|
||||
});
|
||||
|
||||
|
||||
@@ -564,12 +558,9 @@ describe("resource", function() {
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data: [{id: 1}, {id :2}],
|
||||
status: 200,
|
||||
config: {method: 'GET', data: undefined, url: '/CreditCard', params: {key: 'value'}},
|
||||
resource: [ { id : 1 }, { id : 2 } ]
|
||||
});
|
||||
expect(response.data).toEqual([{id: 1}, {id :2}]);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.resource).toEqualData([ { id : 1 }, { id : 2 } ]);
|
||||
expect(typeof response.resource[0].$save).toBe('function');
|
||||
expect(typeof response.resource[1].$save).toBe('function');
|
||||
});
|
||||
@@ -613,11 +604,8 @@ describe("resource", function() {
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data : 'resource not found',
|
||||
status : 404,
|
||||
config : { method : 'GET', data : undefined, url : '/CreditCard', params: {key: 'value'}}
|
||||
});
|
||||
expect(response.data).toEqual('resource not found');
|
||||
expect(response.status).toEqual(404);
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user