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

fix($http): parse JSON only if Content-Type header contains string 'json' #5765

Closed
wants to merge 3 commits into from

Conversation

caitp
Copy link
Contributor

@caitp caitp commented Jan 13, 2014

The default behaviour causes issues with a wide variety of custom
interpolation markers. Even if the JSON test regexps were strengthened to
closer match the standard JSON format, it would still limit the possibilities
of different custom interpolation markers.

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 #5756
Closes #2973

@caitp
Copy link
Contributor Author

caitp commented Jan 13, 2014

Hmmm, this breaks docs unfortunately :( I guess there needs to be a better way to do this

@IgorMinar
Copy link
Contributor

this is going to break a ton of tests as you demonstrated with this PR.

I think we'll need to come up with a different solution :-/

or somehow make the tests generally compatible with this change.

since it's a breaking change we also can't merge it in for a 1.2.x release. so I'm marking it as 1.3

@caitp
Copy link
Contributor Author

caitp commented Jan 14, 2014

I considered wrapping the JSON parsing in a try/catch block, but I think this would hide legitimate errors and be a problem. I'm not sure if there's any other way to do this without some breaking changes. This won't affect JSONP at all, so it's probably for the better to take the Content-Type header into consideration.

The script directive tests which were broken are broken because the cached value has to be an array, otherwise if the value is an array, $http treats it as an array of response values and resolves with those. This could be avoided by storing the string and calling JSON.parse in transformResponse, but I think doing this we could improve performance a little bit (at the cost of memory) --- however it would cause a minor bug in that it would always return the same object and not a copy... So that's kind of not good.

So, maybe the scriptDirective stuff could be improved, but I think the rest of it is probably how it's going to be unless we decide we want to hide legitimate errors

--- something that could be added to make migration for this feature a bit more painless is to make $http methods which specifically expect JSON values and set up the content-type header themselves, so that it's like a 4 letter change per test.

And maybe a helper to put JSON into $templateCache, too. But yeah, no matter what it's still going to break some apps.

This change is needed so that $http can avoid parsing JSON if an inline
template is in use.

BREAKING CHANGE: inline templates which were previously parsed as JSON by
default, now require the attribute `content-type` to contain the text 'json'.

Additionally, the data pushed into $templateCache by the script directive is
now always an array, with the following items:

[ status (200), data (script text or parsed JSON), headers (the empty object) ]

BEFORE:

```
<script type="text/ng-template" id="famous-foods.json">
{
  "elvis presley": "peanut butter, banana & bacon sandwich",
  "shirley temple": "whipped potatoes & roast beef",
  "marilyn monroe": "grilled steak"
}
</script>
```

AFTER:

```
<script type="text/ng-template" id="famous-foods.json" content-type="application/json">
{
  "elvis presley": "peanut butter, banana & bacon sandwich",
  "shirley temple": "whipped potatoes & roast beef",
  "marilyn monroe": "grilled steak"
}
</script>
…son'

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
Helper to assist in migrating to the new $http behaviour. Usage:

```
angular.module('dummy-app', [])
  .config(function($httpProvider) {
    $httpProvider.autoParseJSON(true);
  });
```
@caitp
Copy link
Contributor Author

caitp commented Mar 28, 2014

hmmm ...bitrot.... sigh I'll look at fixing this tomorrow, I think it would be good to ship for one of the 1.3 releases, so people have time to complain about it before it goes stable.

// strip json vulnerability protection prefix
var contentType = isFunction(headers) && headers('Content-Type');
data = data.replace(PROTECTION_PREFIX, '');
if (JSON_START.test(data) && JSON_END.test(data) &&
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use the regexp tests here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove the start and end checks

@IgorMinar
Copy link
Contributor

what's up with this PR? do we want to ship it in 1.3?

@caitp
Copy link
Contributor Author

caitp commented Oct 1, 2014

it definitely makes sense to take content-type response headers into consideration IMO

@caitp caitp closed this Oct 2, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Possible bug with custom interpolates? Detect JSON response by content-type header
4 participants