joente
joente

Reputation: 856

Angular does not encode the semi-colon character in an url

We need an encoded semi-colon character in our url parameter but angular does not encode this character.

The resource we use looks something like this:

app.factory('TestResource', ['$resource', function ($resource) {
    return $resource('http://somedomain.com');
}]);

app.run(['TestResource', function (TestResource) {
    TestResource.query({
        q: 'sin;sout'
    });
}]);

This is the result we get:

http://somedomain.com/?q=sin;sout

We want the url to be:

http://somedomain.com/?q=sin%3Bsout

But if we pre-encode the parameter before sending the % char get's encoded like this:

http://somedomain.com/?q=sin%253Bsout

How can we get the desired result? (http://somedomain.com/?q=sin%3Bsout)

Upvotes: 4

Views: 4367

Answers (4)

cnmuc
cnmuc

Reputation: 6145

There is a closed issue for this https://github.com/angular/angular.js/issues/9224

path: Don't escape semicolons. Breaks matrix parameters, and no evidence that Angular's current behavior is problematic.

query: Escaping the entire query string would be unwise, as it breaks an established (albeit archaic) expectation for servers to be able to handle semicolon-delimited query strings.

query values: Probably safe to escape, especially if the server interprets semicolons as query delimiters. Passing complex data types (ie. JSON) via query parameters is a bad idea, but "real-world" string values often contain semicolons, and it seems like a good idea to escape them.

I'll go with zeroflagL's answer but only changing the query values. Like this:

$provide.decorator('$httpBackend', function($delegate) {
    return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
        var a = document.createElement('a');
        a.href = url;
        if(a.search.length > 1) {
            var params = a.search.substring(1).split('&');
            for (var i=0; i<params.length; i++) {
                var param = params[i].split('=');
                if(param[1]) {
                    params[i] = [param[0], param[1].replace(';', '%3B')].join('=');
                }
            }
            url = url.substring(0, url.lastIndexOf('?')) + '?' + params.join('&');
        }
        $delegate(method, url, post, callback, headers, timeout, withCredentials, responseType);
    };
});

Upvotes: 1

srobinet
srobinet

Reputation: 31

Building on zeroflagL's answer one could consider changing the replace to change all occurrences of the semi-colon (as it is written it will only replace the first semi-colon):

url = url.replace(/;/g, '%3B');

Upvotes: 3

a better oliver
a better oliver

Reputation: 26828

Angular uses its own function to encode the URL. And that function doesn't encode characters that usually don't need to be encoded like / or ?. ; is such a character, too. It's used in matrix URIs, e.g. So Angular's behavior actually conforms to the standard, but it could be a problem when such characters are used literally, i.e. without a special meaning. That seems to be the case here.

Since the URL is build just before sending the request, there's not much we can do. There is kind of a loophole though: Only parameters are encoded, not the base URL. So any workaround would involve creating the URL or at least a part of it yourself.

You could add an interceptor, that adds the properly encoded parameters to the URL before Angular does it. You could even completely replace Angular's behavior that way.

UPDATE:

Another solution came to my mind. The $http service delegates sending the actual request to the $httpBackend service which receives the already constructed URL. Using a decorator you can replace either the ; or the incorrectly encoded %253B with %3B right before the request is sent:

app.config(function($provide) {
  $provide.decorator('$httpBackend', function($delegate) {
    return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
      url = url.replace(';', '%3B');
      $delegate(method, url, post, callback, headers, timeout, withCredentials, responseType);
    };
  })
});

Upvotes: 6

Jai
Jai

Reputation: 74738

You can use encodeURIComponent() method:

q: encodeURIComponent('sin;sout')

Upvotes: 0

Related Questions