Angularjs How uri components are encoded
Asked Answered
D

3

6

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?

Divide answered 2/7, 2014 at 23:4 Comment(0)
A
4

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'));
Arnold answered 19/4, 2015 at 10:33 Comment(1)
Can we access it somehow like angular.encodeUriQuery or something?Nectarous
D
4

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).

Deluge answered 24/6, 2015 at 10:8 Comment(0)
C
-3

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?

Cyanogen answered 2/7, 2014 at 23:31 Comment(3)
Hi - I am passing the parameter pre encoding because I am expecting AngularJS (the js framework I am using here) to do it for me. And it does. But in a way I am not expecting it. It doesn't do the encoding through the encodeURIComponent function. My question is specific to AngularJS.Divide
Please try pre-encoding per my code above. Make the following change to this line. var data = encodeURIComponent('Hello from example.com');Cyanogen
I cannot encode myself the data I pass to the framework for server communication. According to the documentation the $http function should do it transparently. And it actually does it correclty. My problem is mainly about testing, like in the jasmine test I included in my question. Pre-encoding in my code is not acceptable.Divide

© 2022 - 2024 — McMap. All rights reserved.