Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 7c87cc5

Browse files
feat($http): support custom params serializers
1 parent 73f3515 commit 7c87cc5

File tree

3 files changed

+140
-27
lines changed

3 files changed

+140
-27
lines changed

src/AngularPublic.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@
8686
$$AsyncCallbackProvider,
8787
$WindowProvider,
8888
$$jqLiteProvider,
89-
$$CookieReaderProvider
89+
$$CookieReaderProvider,
90+
$$DefaultHttpParamSerializerProvider,
91+
$$JqueryParamSerializerProvider
9092
*/
9193

9294

@@ -243,7 +245,9 @@ function publishExternalAPI(angular) {
243245
$$asyncCallback: $$AsyncCallbackProvider,
244246
$$jqLite: $$jqLiteProvider,
245247
$$HashMap: $$HashMapProvider,
246-
$$cookieReader: $$CookieReaderProvider
248+
$$cookieReader: $$CookieReaderProvider,
249+
$$defaultHttpParamSerializer: $$DefaultHttpParamSerializerProvider,
250+
$$jqueryParamSerializer: $$JqueryParamSerializerProvider
247251
});
248252
}
249253
]);

src/ng/http.js

+68-24
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,57 @@ var JSON_ENDS = {
99
};
1010
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
1111

12+
function paramSerializerFactory(arraySuffix) {
13+
14+
function serializeValue(v) {
15+
if (isObject(v)) {
16+
return isDate(v) ? v.toISOString() : toJson(v);
17+
}
18+
return v;
19+
}
20+
21+
return function paramSerializer(params) {
22+
if (!params) return '';
23+
var parts = [];
24+
forEachSorted(params, function(value, key) {
25+
if (value === null || isUndefined(value)) return;
26+
if (isArray(value)) {
27+
forEach(value, function(v) {
28+
parts.push(encodeUriQuery(key) + (arraySuffix ? '[]' : '') + '=' + encodeUriQuery(serializeValue(v)));
29+
});
30+
} else {
31+
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
32+
}
33+
});
34+
35+
return parts.length > 0 ? parts.join('&') : '';
36+
};
37+
}
38+
39+
/**
40+
* @ngdoc provider
41+
* @name $$DefaultHttpParamSerializerProvider
42+
* @description
43+
*
44+
* */
45+
function $$DefaultHttpParamSerializerProvider() {
46+
this.$get = function() {
47+
return paramSerializerFactory(false);
48+
};
49+
}
50+
51+
/**
52+
* @ngdoc provider
53+
* @name $$JqueryParamSerializerProvider
54+
* @description
55+
*
56+
* */
57+
function $$JqueryParamSerializerProvider() {
58+
this.$get = function() {
59+
return paramSerializerFactory(true);
60+
};
61+
}
62+
1263
function defaultHttpResponseTransform(data, headers) {
1364
if (isString(data)) {
1465
// Strip json vulnerability protection prefix and trim whitespace
@@ -174,7 +225,9 @@ function $HttpProvider() {
174225
},
175226

176227
xsrfCookieName: 'XSRF-TOKEN',
177-
xsrfHeaderName: 'X-XSRF-TOKEN'
228+
xsrfHeaderName: 'X-XSRF-TOKEN',
229+
230+
paramSerializer: '$$defaultHttpParamSerializer'
178231
};
179232

180233
var useApplyAsync = false;
@@ -225,6 +278,12 @@ function $HttpProvider() {
225278

226279
var defaultCache = $cacheFactory('$http');
227280

281+
/**
282+
* Make sure that default param serializer is exposed as a function
283+
*/
284+
defaults.paramSerializer = isString(defaults.paramSerializer) ?
285+
$injector.get(defaults.paramSerializer) : defaults.paramSerializer;
286+
228287
/**
229288
* Interceptors stored in reverse order. Inner interceptors before outer interceptors.
230289
* The reversal is needed so that we can build up the interception chain around the
@@ -764,11 +823,14 @@ function $HttpProvider() {
764823
var config = extend({
765824
method: 'get',
766825
transformRequest: defaults.transformRequest,
767-
transformResponse: defaults.transformResponse
826+
transformResponse: defaults.transformResponse,
827+
paramSerializer: defaults.paramSerializer
768828
}, requestConfig);
769829

770830
config.headers = mergeHeaders(requestConfig);
771831
config.method = uppercase(config.method);
832+
config.paramSerializer = isString(config.paramSerializer) ?
833+
$injector.get(config.paramSerializer) : config.paramSerializer;
772834

773835
var serverRequest = function(config) {
774836
var headers = config.headers;
@@ -1032,7 +1094,7 @@ function $HttpProvider() {
10321094
cache,
10331095
cachedResp,
10341096
reqHeaders = config.headers,
1035-
url = buildUrl(config.url, config.params);
1097+
url = buildUrl(config.url, config.paramSerializer(config.params));
10361098

10371099
$http.pendingRequests.push(config);
10381100
promise.then(removePendingReq, removePendingReq);
@@ -1139,27 +1201,9 @@ function $HttpProvider() {
11391201
}
11401202

11411203

1142-
function buildUrl(url, params) {
1143-
if (!params) return url;
1144-
var parts = [];
1145-
forEachSorted(params, function(value, key) {
1146-
if (value === null || isUndefined(value)) return;
1147-
if (!isArray(value)) value = [value];
1148-
1149-
forEach(value, function(v) {
1150-
if (isObject(v)) {
1151-
if (isDate(v)) {
1152-
v = v.toISOString();
1153-
} else {
1154-
v = toJson(v);
1155-
}
1156-
}
1157-
parts.push(encodeUriQuery(key) + '=' +
1158-
encodeUriQuery(v));
1159-
});
1160-
});
1161-
if (parts.length > 0) {
1162-
url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
1204+
function buildUrl(url, serializedParams) {
1205+
if (serializedParams.length > 0) {
1206+
url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
11631207
}
11641208
return url;
11651209
}

test/ng/httpSpec.js

+66-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
describe('$http', function() {
44

55
var callback, mockedCookies;
6+
var customParamSerializer = function(params) {
7+
return Object.keys(params).join('_');
8+
};
69

710
beforeEach(function() {
811
callback = jasmine.createSpy('done');
@@ -14,6 +17,9 @@ describe('$http', function() {
1417
});
1518
});
1619

20+
beforeEach(module({
21+
customParamSerializer: customParamSerializer
22+
}));
1723
beforeEach(module(function($exceptionHandlerProvider) {
1824
$exceptionHandlerProvider.mode('log');
1925
}));
@@ -354,6 +360,20 @@ describe('$http', function() {
354360
$httpBackend.expect('GET', '/url?date=2014-07-15T17:30:00.000Z').respond('');
355361
$http({url: '/url', params: {date:new Date('2014-07-15T17:30:00.000Z')}, method: 'GET'});
356362
});
363+
364+
365+
describe('custom params serialization', function() {
366+
367+
it('should allow specifying custom paramSerializer as function', function() {
368+
$httpBackend.expect('GET', '/url?foo_bar').respond('');
369+
$http({url: '/url', params: {foo: 'fooVal', bar: 'barVal'}, paramSerializer: customParamSerializer});
370+
});
371+
372+
it('should allow specifying custom paramSerializer as function from DI', function() {
373+
$httpBackend.expect('GET', '/url?foo_bar').respond('');
374+
$http({url: '/url', params: {foo: 'fooVal', bar: 'barVal'}, paramSerializer: 'customParamSerializer'});
375+
});
376+
});
357377
});
358378

359379

@@ -1788,11 +1808,16 @@ describe('$http', function() {
17881808
$httpBackend.flush();
17891809
});
17901810

1791-
it('should have separate opbjects for defaults PUT and POST', function() {
1811+
it('should have separate objects for defaults PUT and POST', function() {
17921812
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.put);
17931813
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.patch);
17941814
expect($http.defaults.headers.put).not.toBe($http.defaults.headers.patch);
17951815
});
1816+
1817+
it('should expose default param serializer at runtime', function() {
1818+
var paramSerializer = $http.defaults.paramSerializer;
1819+
expect(paramSerializer({foo: 'foo', bar: ['bar', 'baz']})).toEqual('bar=bar&bar=baz&foo=foo');
1820+
});
17961821
});
17971822
});
17981823

@@ -1929,3 +1954,43 @@ describe('$http with $applyAsync', function() {
19291954
expect(log).toEqual(['response 1', 'response 2', 'response 3']);
19301955
});
19311956
});
1957+
1958+
describe('$http param serializers', function() {
1959+
1960+
var defSer, jqrSer;
1961+
beforeEach(inject(function($$defaultHttpParamSerializer, $$jqueryParamSerializer) {
1962+
defSer = $$defaultHttpParamSerializer;
1963+
jqrSer = $$jqueryParamSerializer;
1964+
}));
1965+
1966+
describe('common functionality', function() {
1967+
1968+
it('should return empty string for null or undefined params', function() {
1969+
expect(defSer(undefined)).toEqual('');
1970+
expect(jqrSer(undefined)).toEqual('');
1971+
expect(defSer(null)).toEqual('');
1972+
expect(jqrSer(null)).toEqual('');
1973+
});
1974+
1975+
it('should serialize objects', function() {
1976+
expect(defSer({foo: 'foov', bar: 'barv'})).toEqual('bar=barv&foo=foov');
1977+
expect(jqrSer({foo: 'foov', bar: 'barv'})).toEqual('bar=barv&foo=foov');
1978+
});
1979+
1980+
});
1981+
1982+
describe('default array serialization', function() {
1983+
1984+
it('should serialize arrays by repeating param name', function() {
1985+
expect(defSer({a: 'b', foo: ['bar', 'baz']})).toEqual('a=b&foo=bar&foo=baz');
1986+
});
1987+
});
1988+
1989+
describe('jquery array serialization', function() {
1990+
1991+
it('should serialize arrays by repeating param name with [] suffix', function() {
1992+
expect(jqrSer({a: 'b', foo: ['bar', 'baz']})).toEqual('a=b&foo[]=bar&foo[]=baz');
1993+
});
1994+
});
1995+
1996+
});

0 commit comments

Comments
 (0)