I was expecting AngularJS to encode query string parameters using the standard javascript function encodeURIComponent
. According to the following test it is not the case:
describe('$http', function () {
it('encodes uri components correctly', inject(function($http, $httpBackend) {
var data = 'Hello from http://example.com';
$httpBackend.expectGET('/api/process?data=' + encodeURIComponent(data));
$http({ method: 'GET', url: '/api/process', params: { data: data } });
$httpBackend.flush();
}));
});
The test fails with the following error:
$http encodes uri components correctly
Error: Unexpected request: GET /api/process?data=Hello+from+http:%2F%2Fexample.com
Expected GET /api/process?data=Hello%20from%20http%3A%2F%2Fexample.com
To sum up:
- Expected encoding:
Hello%20from%20http%3A%2F%2Fexample.com
- Actual encoding:
Hello+from+http:%2F%2Fexample.com
What uri component (aka query string parameters) encoding method should I expect with AngularJS?
Angular (at least 1.3) doesn't only use encodeURIComponent and changes some replacements (like " " to "+").
this is the commit explaining why :
https://github.com/angular/angular.js/commit/9e30baad3feafc82fb2f2011fd3f21909f4ba29e
And here's what you can see in 1.3 sources :
/**
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
* method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
* encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%3B/gi, ';').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
note that pctEncodeSpaces
is hardcoded to true
;
Here's what you can do to decode URI parameters
decodeURIComponent(val.
replace('@', '%40').
replace(':', '%3A').
replace('$', '%24').
replace(',', '%2C').
replace(';', '%3B').
replace('+', '%20'));
In my humble opinion AngularJS is wrongly encoding in the same way URI path segments AND URI query parameters. To me this is a bug and I actually issued a pull request for fixing it.
The test I introduce in the pull request actually confirms this bug (tested it with both AngularJS 1.3.*
and current master
).
- https://github.com/angular/angular.js/pull/12201
It appears that when you are passing the parameters pre encoding. You encode the URL but after you pass a non-encoded url through the JSON data parameter. Maybe this alteration to your code will work.
describe('$http', function () {
it('encodes uri components correctly', inject(function($http, $httpBackend) {
var data = encodeURIComponent('Hello from http://example.com');
$httpBackend.expectGET('/api/process?data=' + encodeURIComponent(data));
$http({ method: 'GET', url: '/api/process', params: { data: data } });
$httpBackend.flush();
}));
});
Also, After taking only the URL encoding piece and placing it inside a fiddle:
http://jsfiddle.net/eNtgL/1/
It appears to be working correctly, you may want to investigate external factors causing the issue with your URL. There are also several other options described here in this
Encode URL in JavaScript?