Skip to content

Commit 24c3ed0

Browse files
committed
fix($http): don't parse templates as 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 5c2b05e commit 24c3ed0

File tree

3 files changed

+39
-14
lines changed

3 files changed

+39
-14
lines changed

src/ng/http.js

+6-3
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;
@@ -133,7 +135,8 @@ function $HttpProvider() {
133135
var responseInterceptorFactories = this.responseInterceptors = [];
134136

135137
this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
136-
function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
138+
'$templateCache',
139+
function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector, $templateCache) {
137140

138141
var defaultCache = $cacheFactory('$http');
139142

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 tpl from $templateCache', inject(function($templateCache, $rootScope) {
1079+
$templateCache.put('foo.json', '{ "foo": "bar" }');
1080+
$http.get('foo.json', { cache: $templateCache }).success(callback);
1081+
$rootScope.$digest();
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)