Skip to content

Commit 69a326c

Browse files
committed
feat(http): add multiple parameters serializers
Add the ability to add parameters serializers Add common parameters serializers Closes angular#7363
1 parent be7c02c commit 69a326c

File tree

2 files changed

+167
-22
lines changed

2 files changed

+167
-22
lines changed

src/ng/http.js

+77-22
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ function $HttpProvider() {
117117
},
118118

119119
xsrfCookieName: 'XSRF-TOKEN',
120-
xsrfHeaderName: 'X-XSRF-TOKEN'
120+
xsrfHeaderName: 'X-XSRF-TOKEN',
121+
paramsSerializer: 'keyValue'
121122
};
122123

123124
/**
@@ -126,6 +127,62 @@ function $HttpProvider() {
126127
*/
127128
var interceptorFactories = this.interceptors = [];
128129

130+
/**
131+
* The URL parameters serializers
132+
*/
133+
var paramsSerializers = this.paramsSerializers = {
134+
keyValue: keyValueParametersSerializer,
135+
JSON: jsonParametersSerializer,
136+
nonShallowJSON: nonShallowJSONParametersSerializer,
137+
_default: keyValueParametersSerializer
138+
};
139+
140+
function keyValueParametersSerializer(params, callback) {
141+
forEachSorted(params, function(value, key) {
142+
if (value === null || isUndefined(value)) return;
143+
if (!isArray(value)) value = [value];
144+
145+
forEach(value, function(v) {
146+
if (isObject(v)) {
147+
v = toJson(v);
148+
}
149+
callback(key, v);
150+
});
151+
});
152+
}
153+
154+
function jsonParametersSerializer(params, callback) {
155+
forEachSorted(params, function(value, key) {
156+
if (value === null || isUndefined(value)) return;
157+
if (isArray(value) || isObject(value)) {
158+
value = toJson(value);
159+
}
160+
callback(key, value);
161+
});
162+
}
163+
164+
function nonShallowJSONParametersSerializer(params, callback) {
165+
function serialize(toSerialize, prefix, topLevel) {
166+
if (toSerialize === null || isUndefined(toSerialize)) return;
167+
if (isArray(toSerialize)) {
168+
forEach(toSerialize, function(value) {
169+
serialize(value, prefix + '[]');
170+
});
171+
} else if (isObject(toSerialize)) {
172+
forEachSorted(toSerialize, function(value, key) {
173+
serialize(value, prefix +
174+
(topLevel ? '' : '[') +
175+
key +
176+
(topLevel ? '' : ']'));
177+
});
178+
} else {
179+
callback(prefix, toSerialize);
180+
}
181+
}
182+
183+
serialize(params, '', true);
184+
}
185+
129186
this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
130187
function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
131188

@@ -594,7 +651,8 @@ function $HttpProvider() {
594651
var config = {
595652
method: 'get',
596653
transformRequest: defaults.transformRequest,
597-
transformResponse: defaults.transformResponse
654+
transformResponse: defaults.transformResponse,
655+
paramsSerializer: defaults.paramsSerializer
598656
};
599657
var headers = mergeHeaders(requestConfig);
600658

@@ -852,7 +910,7 @@ function $HttpProvider() {
852910
promise = deferred.promise,
853911
cache,
854912
cachedResp,
855-
url = buildUrl(config.url, config.params);
913+
url = buildUrl(config.url, config.params, config.paramsSerializer);
856914

857915
$http.pendingRequests.push(config);
858916
promise.then(removePendingReq, removePendingReq);
@@ -939,26 +997,23 @@ function $HttpProvider() {
939997
}
940998

941999

942-
function buildUrl(url, params) {
943-
if (!params) return url;
944-
var parts = [];
945-
forEachSorted(params, function(value, key) {
946-
if (value === null || isUndefined(value)) return;
947-
if (!isArray(value)) value = [value];
1000+
function buildUrl(url, params, paramsSerializer) {
1001+
if (!params) return url;
9481002

949-
forEach(value, function(v) {
950-
if (isObject(v)) {
951-
v = toJson(v);
952-
}
953-
parts.push(encodeUriQuery(key) + '=' +
954-
encodeUriQuery(v));
955-
});
956-
});
957-
if(parts.length > 0) {
958-
url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
959-
}
960-
return url;
961-
}
1003+
var parts = [];
1004+
if (!isFunction(paramsSerializer)) {
1005+
paramsSerializer = paramsSerializers[paramsSerializer] ||
1006+
paramsSerializers._default;
1007+
}
1008+
1009+
paramsSerializer(params, function(key, value) {
1010+
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(value));
1011+
});
1012+
if(parts.length > 0) {
1013+
url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
1014+
}
1015+
return url;
1016+
}
9621017

9631018

9641019
}];

test/ng/httpSpec.js

+90
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ describe('$http', function() {
274274
describe('the instance', function() {
275275
var $httpBackend, $http, $rootScope;
276276

277+
beforeEach(module(function($httpProvider) {
278+
$httpProvider.paramsSerializers.custom = function(params, callback) {
279+
callback('foo', 'bar');
280+
};
281+
}));
282+
277283
beforeEach(inject(['$rootScope', function($rs) {
278284
$rootScope = $rs;
279285

@@ -341,6 +347,90 @@ describe('$http', function() {
341347
$httpBackend.expect('GET', '/url').respond('');
342348
$http({url: '/url', params: {}, method: 'GET'});
343349
});
350+
351+
describe('params serialization', function() {
352+
it('should be possible to use key-value serialization', inject(function($httpBackend, $http) {
353+
var testCases = [
354+
{
355+
params: {a: 1, b: 2},
356+
serialized: 'a=1&b=2'
357+
},
358+
{
359+
params: {a: 1, b: [2, 3]},
360+
serialized: 'a=1&b=2&b=3'
361+
},
362+
{
363+
params: {a: 'abc', b: {foo: 'bar'}},
364+
serialized: 'a=abc&b=%7B%22foo%22:%22bar%22%7D'
365+
}
366+
];
367+
angular.forEach(testCases, function(testCase) {
368+
$httpBackend.expect('GET', '/url?' + testCase.serialized).respond('');
369+
$http({url: '/url', params: testCase.params, method: 'GET', paramsSerializer: 'keyValue'});
370+
});
371+
}));
372+
373+
it('should be possible to use JSON serialization', inject(function($httpBackend, $http) {
374+
var testCases = [
375+
{
376+
params: {a: 1, b: 2},
377+
serialized: 'a=1&b=2'
378+
},
379+
{
380+
params: {a: 1, b: [2, 3]},
381+
serialized: 'a=1&b=%5B2,3%5D'
382+
},
383+
{
384+
params: {a: 'abc', b: {foo: 'bar'}},
385+
serialized: 'a=abc&b=%7B%22foo%22:%22bar%22%7D'
386+
}
387+
];
388+
angular.forEach(testCases, function(testCase) {
389+
$httpBackend.expect('GET', '/url?' + testCase.serialized).respond('');
390+
$http({url: '/url', params: testCase.params, method: 'GET', paramsSerializer: 'JSON'});
391+
});
392+
}));
393+
394+
it('should be possible to use nonShallowJSON serialization', inject(function($httpBackend, $http) {
395+
var testCases = [
396+
{
397+
params: {a: 1, b: 2},
398+
serialized: 'a=1&b=2'
399+
},
400+
{
401+
params: {a: 1, b: [2, 3]},
402+
serialized: 'a=1&b%5B%5D=2&b%5B%5D=3' // a=1&b[]=2&b[]=3
403+
},
404+
{
405+
params: {a: 1, b: [2, {foo: 'bar'}]},
406+
serialized: 'a=1&b%5B%5D=2&b%5B%5D%5Bfoo%5D=bar' // a=1&b[]=2&b[][foo]=bar
407+
},
408+
{
409+
params: {a: 'abc', b: {foo: 'bar', man: 'shell'}},
410+
serialized: 'a=abc&b%5Bfoo%5D=bar&b%5Bman%5D=shell' // a=abc&b[foo]=bar&b[man]=shell
411+
}
412+
];
413+
angular.forEach(testCases, function(testCase) {
414+
$httpBackend.expect('GET', '/url?' + testCase.serialized).respond('');
415+
$http({url: '/url', params: testCase.params, method: 'GET', paramsSerializer: 'nonShallowJSON'});
416+
});
417+
418+
}));
419+
420+
it('should be possible to use a custom serialization', inject(function($httpBackend, $http) {
421+
function serializer(params, callback) {
422+
callback('foo', 'bar');
423+
}
424+
425+
$httpBackend.expect('GET', '/url?foo=bar').respond('');
426+
$http({url: '/url', params: {}, method: 'GET', paramsSerializer: serializer});
427+
}));
428+
429+
it('should be possible to use a predefined custom serializer', inject(function($httpBackend, $http) {
430+
$httpBackend.expect('GET', '/url?foo=bar').respond('');
431+
$http({url: '/url', params: {}, method: 'GET', paramsSerializer: 'custom'});
432+
}));
433+
});
344434
});
345435

346436

0 commit comments

Comments
 (0)