diff --git a/modules/angular2/src/router/rules/route_paths/param_route_path.ts b/modules/angular2/src/router/rules/route_paths/param_route_path.ts index e854874965..fc7daf3171 100644 --- a/modules/angular2/src/router/rules/route_paths/param_route_path.ts +++ b/modules/angular2/src/router/rules/route_paths/param_route_path.ts @@ -62,7 +62,7 @@ class DynamicPathSegment implements PathSegment { throw new BaseException( `Route generator for '${this.name}' was not included in parameters passed.`); } - return normalizeString(params.get(this.name)); + return encodeDynamicSegment(normalizeString(params.get(this.name))); } } @@ -130,7 +130,7 @@ export class ParamRoutePath implements RoutePath { captured.push(currentUrlSegment.path); if (pathSegment instanceof DynamicPathSegment) { - positionalParams[pathSegment.name] = currentUrlSegment.path; + positionalParams[pathSegment.name] = decodeDynamicSegment(currentUrlSegment.path); } else if (!pathSegment.match(currentUrlSegment.path)) { return null; } @@ -269,3 +269,43 @@ export class ParamRoutePath implements RoutePath { } static RESERVED_CHARS = RegExpWrapper.create('//|\\(|\\)|;|\\?|='); } + +let REGEXP_PERCENT = /%/g; +let REGEXP_SLASH = /\//g; +let REGEXP_OPEN_PARENT = /\(/g; +let REGEXP_CLOSE_PARENT = /\)/g; +let REGEXP_SEMICOLON = /;/g; + +function encodeDynamicSegment(value: string): string { + if (isBlank(value)) { + return null; + } + + value = StringWrapper.replaceAll(value, REGEXP_PERCENT, '%25'); + value = StringWrapper.replaceAll(value, REGEXP_SLASH, '%2F'); + value = StringWrapper.replaceAll(value, REGEXP_OPEN_PARENT, '%28'); + value = StringWrapper.replaceAll(value, REGEXP_CLOSE_PARENT, '%29'); + value = StringWrapper.replaceAll(value, REGEXP_SEMICOLON, '%3B'); + + return value; +} + +let REGEXP_ENC_SEMICOLON = /%3B/ig; +let REGEXP_ENC_CLOSE_PARENT = /%29/ig; +let REGEXP_ENC_OPEN_PARENT = /%28/ig; +let REGEXP_ENC_SLASH = /%2F/ig; +let REGEXP_ENC_PERCENT = /%25/ig; + +function decodeDynamicSegment(value: string): string { + if (isBlank(value)) { + return null; + } + + value = StringWrapper.replaceAll(value, REGEXP_ENC_SEMICOLON, ';'); + value = StringWrapper.replaceAll(value, REGEXP_ENC_CLOSE_PARENT, ')'); + value = StringWrapper.replaceAll(value, REGEXP_ENC_OPEN_PARENT, '('); + value = StringWrapper.replaceAll(value, REGEXP_ENC_SLASH, '/'); + value = StringWrapper.replaceAll(value, REGEXP_ENC_PERCENT, '%'); + + return value; +} diff --git a/modules/angular2/test/router/rules/route_paths/param_route_path_spec.ts b/modules/angular2/test/router/rules/route_paths/param_route_path_spec.ts index 94eaaf888d..adb81c879e 100644 --- a/modules/angular2/test/router/rules/route_paths/param_route_path_spec.ts +++ b/modules/angular2/test/router/rules/route_paths/param_route_path_spec.ts @@ -50,6 +50,33 @@ export function main() { }); }); + describe('dynamic segments', () => { + it('should parse parameters', () => { + var rec = new ParamRoutePath('/test/:id'); + var url = new Url('test', new Url('abc')); + var match = rec.matchUrl(url); + expect(match.allParams).toEqual({'id': 'abc'}); + }); + + it('should decode special characters when parsing', () => { + var rec = new ParamRoutePath('/test/:id'); + var url = new Url('test', new Url('abc%25%2F%2f%28%29%3B')); + var match = rec.matchUrl(url); + expect(match.allParams).toEqual({'id': 'abc%//();'}); + }); + + it('should generate url', () => { + var rec = new ParamRoutePath('/test/:id'); + expect(rec.generateUrl({'id': 'abc'}).urlPath).toEqual('test/abc'); + }); + + it('should encode special characters when generating', () => { + var rec = new ParamRoutePath('/test/:id'); + expect(rec.generateUrl({'id': 'abc/def/%();'}).urlPath) + .toEqual('test/abc%2Fdef%2F%25%28%29%3B'); + }); + }); + describe('matrix params', () => { it('should be parsed along with dynamic paths', () => { var rec = new ParamRoutePath('/hello/:id');