fix($templateRequest): ignore JSON Content-Type header and content

Normally, if there is a Content-Type header with the string "application/json", or else the content
looks sort of JSON-y, $http will attempt to deserialize the JSON into an object. $templateRequest
is intended to request markup, and as such should never attempt to parse JSON, regardless of the
headers or shape of the content.

Closes #5756
Closes #9619
This commit is contained in:
Caitlin Potter
2014-10-14 14:51:29 -04:00
parent 9078a6ae37
commit 1bd473eb45
4 changed files with 88 additions and 19 deletions

View File

@@ -100,6 +100,8 @@
"NODE_TYPE_DOCUMENT": false,
"NODE_TYPE_DOCUMENT_FRAGMENT": false,
"httpResponseTransform": false,
/* filters.js */
"getFirstThursdayOfYear": false,

View File

@@ -1,5 +1,24 @@
'use strict';
var APPLICATION_JSON = 'application/json';
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
var JSON_START = /^\s*(\[|\{[^\{])/;
var JSON_END = /[\}\]]\s*$/;
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
function defaultHttpResponseTransform(data, headers) {
if (isString(data)) {
// strip json vulnerability protection prefix
data = data.replace(JSON_PROTECTION_PREFIX, '');
var contentType = headers('Content-Type');
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) ||
(JSON_START.test(data) && JSON_END.test(data))) {
data = fromJson(data);
}
}
return data;
}
/**
* Parse headers into key value object
*
@@ -86,12 +105,6 @@ function isSuccess(status) {
* Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
* */
function $HttpProvider() {
var JSON_START = /^\s*(\[|\{[^\{])/,
JSON_END = /[\}\]]\s*$/,
PROTECTION_PREFIX = /^\)\]\}',?\n/,
APPLICATION_JSON = 'application/json',
CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
/**
* @ngdoc property
* @name $httpProvider#defaults
@@ -115,18 +128,7 @@ function $HttpProvider() {
**/
var defaults = this.defaults = {
// transform incoming response data
transformResponse: [function defaultHttpResponseTransform(data, headers) {
if (isString(data)) {
// strip json vulnerability protection prefix
data = data.replace(PROTECTION_PREFIX, '');
var contentType = headers('Content-Type');
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) ||
(JSON_START.test(data) && JSON_END.test(data))) {
data = fromJson(data);
}
}
return data;
}],
transformResponse: [defaultHttpResponseTransform],
// transform outgoing request data
transformRequest: [function(d) {

View File

@@ -25,7 +25,28 @@ function $TemplateRequestProvider() {
var self = handleRequestFn;
self.totalPendingRequests++;
return $http.get(tpl, { cache : $templateCache })
var transformResponse = $http.defaults && $http.defaults.transformResponse;
var idx;
if (isArray(transformResponse)) {
var original = transformResponse;
transformResponse = [];
for (var i=0; i<original.length; ++i) {
var transformer = original[i];
if (transformer !== defaultHttpResponseTransform) {
transformResponse.push(transformer);
}
}
} else if (transformResponse === defaultHttpResponseTransform) {
transformResponse = null;
}
var httpOptions = {
cache: $templateCache,
transformResponse: transformResponse
};
return $http.get(tpl, httpOptions)
.then(function(response) {
var html = response.data;
if(!html || html.length === 0) {

View File

@@ -86,4 +86,48 @@ describe('$templateRequest', function() {
expect($templateRequest.totalPendingRequests).toBe(0);
}));
it('should not try to parse a response as JSON',
inject(function($templateRequest, $httpBackend) {
var spy = jasmine.createSpy('success');
$httpBackend.expectGET('a.html').respond('{{text}}', {
'Content-Type': 'application/json'
});
$templateRequest('a.html').then(spy);
$httpBackend.flush();
expect(spy).toHaveBeenCalledOnceWith('{{text}}');
}));
it('should use custom response transformers (array)', function() {
module(function($httpProvider) {
$httpProvider.defaults.transformResponse.push(function(data) {
return data + '!!';
});
});
inject(function($templateRequest, $httpBackend) {
var spy = jasmine.createSpy('success');
$httpBackend.expectGET('a.html').respond('{{text}}', {
'Content-Type': 'application/json'
});
$templateRequest('a.html').then(spy);
$httpBackend.flush();
expect(spy).toHaveBeenCalledOnceWith('{{text}}!!');
});
});
it('should use custom response transformers (function)', function() {
module(function($httpProvider) {
$httpProvider.defaults.transformResponse = function(data) {
return data + '!!';
};
});
inject(function($templateRequest, $httpBackend) {
var spy = jasmine.createSpy('success');
$httpBackend.expectGET('a.html').respond('{{text}}', {
'Content-Type': 'application/json'
});
$templateRequest('a.html').then(spy);
$httpBackend.flush();
expect(spy).toHaveBeenCalledOnceWith('{{text}}!!');
});
});
});