Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS route parameters with any characters

I am new to AngularJS, so forgive me if this is obvious, but I am looking for someone who can answer this tricky question. I am implementing an application, and need to pass some parameters to a particular view to display details about a book. Basically I would like to be able to use the following routing expressions:

bookApp.config(['$routeProvider', function($routeProvider) {
    $routeProvider.
        when('/catalog', {
            templateUrl: 'cataloglist.htm',
            controller: 'catCtrl'
        }).
        when('/book/:title/:chapter', {
            template: 'chapterdetail.htm',
            controller: 'chapterCtrl'
        }).
        otherwise({
            template: 'oops ... do not understand that route',
        });
    }]);

The expression /book/:title/:chapter should allow me to pass in the name of the title of a book. I am expecting to be able to pass ANY title of any book in there. To make sure that things are properly delimited, I am going to URL encode the title value, so that in the encoded value there will be no slashes and the value will be clearly delimited by the slash characters. This is the normal way to construct URLs that contain values.

The problem is that there exist book titles that contain the slash character (e.g. The 3/5 solution) This is URL encoded as The+3%2F5+Solution. So can construct a URL like this:

 /app/#/book/The+3%2F5+Solution/The%20Beginning

However, my experience seems to show that the entire value is URL decoded BEFORE it is broken into parameters! This means that any data value with a slash in it, will be misinterpreted as two values, and the pattern matching of the route parameters is broken, and only the first half of the value is passed in. Furthermore, the chapter might have a slash in the name as well.

If I was making a REST service, I would URL encode the value, and the URL will be parsed into pieces BEFORE each piece is decoded. For example, I can use query parameters in a URL like this:

app.jsp?title=The+3%2F5+Solution&chapter=The%20Beginning

and this will work correctly. Using URL encoding, I can pass ANY string value in the title. I would have expected route parameters to do the same thing ... but I already mentioned I am NEW to AngularJS.

To decode the %2F into a slash BEFORE determining the pieces seems like a very serious bug. Apparently, you simply can't pass values with a slash in them as route parameters. Am I missing something here? What is the solution to allow me to safely pass a book name with ANY possible character in it (along with a chapter title with ANY character in it), as a route parameter?

like image 668
AgilePro Avatar asked Dec 24 '14 07:12

AgilePro


2 Answers

Taking a look at the angular's route.js sourcecode, there's described a possibility to achieve what you are looking for:

path can contain named groups starting with a colon and ending with a star: e.g.:name*. All characters are eagerly stored in $routeParams under the given name when the route matches.

For example, routes like /color/:color/largecode/:largecode*\/edit will match /color/brown/largecode/code/with/slashes/edit and extract:
color: brown
largecode: code/with/slashes.

Notice the backslash at the end of the :largecode*\ param in the example. It's not present in the description of named groups ending with a star, but it's there in the example. I didn't test this and don't know whether that backslash is required, so take into account that it it might/might not be required.

Thus, a solution to your question will look like: /book/:title*/chapter/:chapter*

Notice the added /chapter/ part. It is required in order to differentiate between the two named groups. If you'll just use /book/:title*/:chapter*, everything will fall under the :title* named group. However, if you use /:title*/chapter/:chapter*, angular knows when :title* named group ends: when it encounters /chapter/ in the route. Everything after the /chapter/ will fall under the :chapter* named group

like image 189
iulian Avatar answered Sep 27 '22 20:09

iulian


The appears to be that you can not use URL Encoding because of the behavior above. This means that you must use an encoding that will represent the full Unicode character set without ever using a slash or a percent sign or a plus sign nor anything else that might cause the URL decoder to think there is an encoded value there to be decoded.

BASE 64 encoding of the value will do the trick, and the solution seems to be to define the following filters:

    bookApp.filter('btoa', function() {
        return function (str) {
            return window.btoa(encodeURIComponent(escape(str)));
        }
    });

    bookApp.filter('atob', function() {
        return function(str) {
            return unescape(decodeURIComponent(window.atob(str)));
        }
    });

This allows you to write code like this:

<a href="#/book/{{title | atob}}/{{chapter | atob}}">See Details</a>

Then, on the receiving side, you call btoa to get the value back to use for whatever purpose you want. The value The 3/5 solution is then encoded as VGhlJTI1MjAzJTJGNSUyNTIwc29sdXRpb24= which will always be a single parameter in the route parameter pattern recognizer, and is decoded exactly back into the value that was sent.

Full support for languages. A book named 奥の細道 will be encoded as JTI1dTU5NjUlMjV1MzA2RSUyNXU3RDMwJTI1dTkwNTM= -- again avoiding any problem that might corrupt the value.

The extra encodeURIComponent and escape calls are there to trick the JS to convert the unicode string into UTF-8 encoding, which is then transformed by the window.atob function, which would otherwise fail with certain higher order Unicode characters. More information at Mozilla Developer Network

like image 31
AgilePro Avatar answered Sep 27 '22 20:09

AgilePro