Skip to content

Commit 8d1dc14

Browse files
committed
fix($http): parse JSON only if Content-Type header contains string 'json'
The default behaviour breaks a wide variety of custom interpolation markers. Even if the JSON text regexps were strengthened to closer match the standard JSON format, it would still limit the possibilities of different custom interpolation markers. Instead, $http will no longer parse JSON if the response Content-Type header does not include the term 'json', or if the $templateCache is used. For inline templates, use the `content-type="json"` attribute to ensure that inline JSON templates are parsed. BREAKING CHANGE: Previously, responses would be parsed as JSON if their content looked vaguely like JSON. Now, they are only parsed as JSON if their Content-Type response header contains the term 'json', and if they are not coming from the $templateCache. Closes angular#5756 Closes angular#2973
1 parent 5aa7877 commit 8d1dc14

File tree

3 files changed

+37
-13
lines changed

3 files changed

+37
-13
lines changed

src/ng/http.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,13 @@ function $HttpProvider() {
9191

9292
var defaults = this.defaults = {
9393
// transform incoming response data
94-
transformResponse: [function(data) {
94+
transformResponse: [function(data, headers) {
9595
if (isString(data)) {
9696
// strip json vulnerability protection prefix
97+
var contentType = isFunction(headers) && headers('Content-Type');
9798
data = data.replace(PROTECTION_PREFIX, '');
98-
if (JSON_START.test(data) && JSON_END.test(data))
99+
if (JSON_START.test(data) && JSON_END.test(data) &&
100+
(contentType && contentType.indexOf('json') >= 0))
99101
data = fromJson(data);
100102
}
101103
return data;

test/ng/httpSpec.js

+30-10
Original file line numberDiff line numberDiff line change
@@ -1014,8 +1014,10 @@ describe('$http', function() {
10141014

10151015
describe('default', function() {
10161016

1017-
it('should deserialize json objects', function() {
1018-
$httpBackend.expect('GET', '/url').respond('{"foo":"bar","baz":23}');
1017+
it('should deserialize json objects with json content-type', function() {
1018+
$httpBackend.expect('GET', '/url').respond('{"foo":"bar","baz":23}', {
1019+
'Content-Type': 'application/json'
1020+
});
10191021
$http({method: 'GET', url: '/url'}).success(callback);
10201022
$httpBackend.flush();
10211023

@@ -1024,8 +1026,10 @@ describe('$http', function() {
10241026
});
10251027

10261028

1027-
it('should deserialize json arrays', function() {
1028-
$httpBackend.expect('GET', '/url').respond('[1, "abc", {"foo":"bar"}]');
1029+
it('should deserialize json arrays with json content-type', function() {
1030+
$httpBackend.expect('GET', '/url').respond('[1, "abc", {"foo":"bar"}]', {
1031+
'Content-Type': 'application/json'
1032+
});
10291033
$http({method: 'GET', url: '/url'}).success(callback);
10301034
$httpBackend.flush();
10311035

@@ -1034,8 +1038,10 @@ describe('$http', function() {
10341038
});
10351039

10361040

1037-
it('should deserialize json with security prefix', function() {
1038-
$httpBackend.expect('GET', '/url').respond(')]}\',\n[1, "abc", {"foo":"bar"}]');
1041+
it('should deserialize json with security prefix with json content-type', function() {
1042+
$httpBackend.expect('GET', '/url').respond(')]}\',\n[1, "abc", {"foo":"bar"}]', {
1043+
'Content-Type': 'application/json'
1044+
});
10391045
$http({method: 'GET', url: '/url'}).success(callback);
10401046
$httpBackend.flush();
10411047

@@ -1044,8 +1050,10 @@ describe('$http', function() {
10441050
});
10451051

10461052

1047-
it('should deserialize json with security prefix ")]}\'"', function() {
1048-
$httpBackend.expect('GET', '/url').respond(')]}\'\n\n[1, "abc", {"foo":"bar"}]');
1053+
it('should deserialize json with security prefix ")]}\'" with json content-type', function() {
1054+
$httpBackend.expect('GET', '/url').respond(')]}\'\n\n[1, "abc", {"foo":"bar"}]', {
1055+
'Content-Type': 'application/json'
1056+
});
10491057
$http({method: 'GET', url: '/url'}).success(callback);
10501058
$httpBackend.flush();
10511059

@@ -1054,8 +1062,10 @@ describe('$http', function() {
10541062
});
10551063

10561064

1057-
it('should not deserialize tpl beginning with ng expression', function() {
1058-
$httpBackend.expect('GET', '/url').respond('{{some}}');
1065+
it('should not deserialize tpl beginning with ng expression with json content-type', function() {
1066+
$httpBackend.expect('GET', '/url').respond('{{some}}', {
1067+
'Content-Type': 'application/json'
1068+
});
10591069
$http.get('/url').success(callback);
10601070
$httpBackend.flush();
10611071

@@ -1065,6 +1075,16 @@ describe('$http', function() {
10651075
});
10661076

10671077

1078+
it('should not deserialize JSON response without Content-Type header matching \'json\'', function() {
1079+
$httpBackend.expect('GET', '/url').respond('{ "foo": "bar" }');
1080+
$http.get('.url').success(callback);
1081+
$httpBackend.flush();
1082+
1083+
expect(callback).toHaveBeenCalledOnce();
1084+
expect(callback.mostRecentCall.args[0]).toBe('{ "foo": "bar" }');
1085+
});
1086+
1087+
10681088
it('should have access to response headers', function() {
10691089
$httpBackend.expect('GET', '/url').respond(200, 'response', {h1: 'header1'});
10701090
$http.get('/url', {

test/ngResource/resourceSpec.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,9 @@ describe("resource", function() {
534534
it('should exercise full stack', function() {
535535
var Person = $resource('/Person/:id');
536536

537-
$httpBackend.expect('GET', '/Person/123').respond('\n{\n"name":\n"misko"\n}\n');
537+
$httpBackend.expect('GET', '/Person/123').respond('\n{\n"name":\n"misko"\n}\n', {
538+
'Content-Type': 'application/json'
539+
});
538540
var person = Person.get({id:123});
539541
$httpBackend.flush();
540542
expect(person.name).toEqual('misko');

0 commit comments

Comments
 (0)