From 10739675cdace3e9555c8751d84df9f0a9ad82d7 Mon Sep 17 00:00:00 2001 From: Sebastien Armand - sa250111 Date: Wed, 19 Feb 2014 17:59:02 +0800 Subject: [PATCH] feat($location): parse query parameters delimited by ; or & #6167 According to RFC (http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2.2) location should match either ';' OR '&', but not both at the same time. Closes #6140 --- src/Angular.js | 5 +++-- src/ng/location.js | 16 ++++++++++++---- test/ng/locationSpec.js | 26 +++++++++++++++++++------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/Angular.js b/src/Angular.js index d22217ecf8b3..8a41af59cd7b 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -1055,9 +1055,10 @@ function tryDecodeURIComponent(value) { * Parses an escaped url query string into key-value pairs. * @returns Object.<(string|boolean)> */ -function parseKeyValue(/**string*/keyValue) { +function parseKeyValue(/**string*/keyValue, delimiter) { + delimiter = delimiter === ';' ? delimiter : '&'; var obj = {}, key_value, key; - forEach((keyValue || "").split(/[&;]/), function(keyValue){ + forEach((keyValue || "").split(delimiter), function(keyValue){ if ( keyValue ) { key_value = keyValue.split('='); key = tryDecodeURIComponent(key_value[0]); diff --git a/src/ng/location.js b/src/ng/location.js index 09bde56762b8..94ca46a2d145 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -39,7 +39,7 @@ function parseAppUrl(relativeUrl, locationObj, appBase) { var match = urlResolve(relativeUrl, appBase); locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname); - locationObj.$$search = parseKeyValue(match.search); + locationObj.$$search = parseKeyValue(match.search, locationObj.$$queryDelimiter); locationObj.$$hash = decodeURIComponent(match.hash); // make sure path starts with '/'; @@ -90,6 +90,7 @@ function serverBase(url) { function LocationHtml5Url(appBase, basePrefix, queryDelimiter) { this.$$html5 = true; basePrefix = basePrefix || ''; + this.$$queryDelimiter = queryDelimiter; var appBaseNoFile = stripFile(appBase); parseAbsoluteUrl(appBase, this, appBase); @@ -120,7 +121,7 @@ function LocationHtml5Url(appBase, basePrefix, queryDelimiter) { * @private */ this.$$compose = function() { - var search = toKeyValue(this.$$search, queryDelimiter), + var search = toKeyValue(this.$$search, this.$$queryDelimiter), hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; @@ -157,6 +158,7 @@ function LocationHtml5Url(appBase, basePrefix, queryDelimiter) { */ function LocationHashbangUrl(appBase, hashPrefix, queryDelimiter) { var appBaseNoFile = stripFile(appBase); + this.$$queryDelimiter = queryDelimiter; parseAbsoluteUrl(appBase, this, appBase); @@ -227,7 +229,7 @@ function LocationHashbangUrl(appBase, hashPrefix, queryDelimiter) { * @private */ this.$$compose = function() { - var search = toKeyValue(this.$$search, queryDelimiter), + var search = toKeyValue(this.$$search, this.$$queryDelimiter), hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; @@ -287,6 +289,12 @@ LocationHashbangInHtml5Url.prototype = */ $$replace: false, + /** + * Allows using ";" instead of "&" to separate query string arguments + * @private + */ + $$queryDelimiter: '&', + /** * @ngdoc method * @name $location#absUrl @@ -415,7 +423,7 @@ LocationHashbangInHtml5Url.prototype = return this.$$search; case 1: if (isString(search)) { - this.$$search = parseKeyValue(search); + this.$$search = parseKeyValue(search, this.$$queryDelimiter); } else if (isObject(search)) { this.$$search = search; } else { diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index 912e753e5de5..add3a2578aaf 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -316,12 +316,18 @@ describe('$location', function() { it('should decode query params delimited interchangeably by & and ;', function() { - var url = new LocationHtml5Url('http://host.com/'); - url.$$parse('http://host.com/?foo=1&bar=2;baz=3'); + var url = new LocationHashbangUrl('http://host.com/', '#'); + url.$$parse('http://host.com/#?foo=1&bar=2;baz'); expect(url.search()).toEqual({ 'foo': '1', - 'bar': '2', - 'baz': '3' + 'bar': '2;baz' + }); + + url = new LocationHashbangUrl('http://host.com/', '#', ';'); + url.$$parse('http://host.com/#?foo=1&bar=2;baz'); + expect(url.search()).toEqual({ + 'foo': '1&bar', + 'baz': true }); }); @@ -469,11 +475,17 @@ describe('$location', function() { it('should decode query params delimited interchangeably by & and ;', function() { var url = new LocationHashbangUrl('http://host.com/', '#'); - url.$$parse('http://host.com/#?foo=1&bar=2;baz=3'); + url.$$parse('http://host.com/#?foo=1&bar=2;baz'); expect(url.search()).toEqual({ 'foo': '1', - 'bar': '2', - 'baz': '3' + 'bar': '2;baz' + }); + + url = new LocationHashbangUrl('http://host.com/', '#', ';'); + url.$$parse('http://host.com/#?foo=1&bar=2;baz'); + expect(url.search()).toEqual({ + 'foo': '1&bar', + 'baz': true }); });