最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AngularJS route parameters with any characters - Stack Overflow

programmeradmin3浏览0评论

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?

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?

Share Improve this question edited Jun 23, 2017 at 17:08 laser 1,37613 silver badges14 bronze badges asked Dec 24, 2014 at 7:29 AgileProAgilePro 5,5984 gold badges37 silver badges60 bronze badges 8
  • likely better off just creating slugs for url's like most sites do – charlietfl Commented Dec 24, 2014 at 7:34
  • If I understand your suggestion, a 'slug' is usually a non-reversable conversion such as used for blog post paths: you throw away all the difficult characters and spaces, and get a cleaned up path which is analogous to the orig value. However, I need to look the passed value up in a database, and while the book title is unique, a simplified version of the title is not necessarily unique, which might prevent me from being able to display information about a book. I simply want to pass a DB value exactly. Maybe it simply can't be done. – AgilePro Commented Dec 24, 2014 at 7:48
  • use an ID instead or create the slug when the title is added to db and use the slug as an index. The url doesn't have to contain the best identifier field for the database – charlietfl Commented Dec 24, 2014 at 7:49
  • Well, yes, the whole theory behind a relational DB is that you use the actual key values in the DB to query the data. However, if route parameters are arbitrarily limited to exclude certain characters it means there are some DB values you can not search for. Forcing one to add another column with a "record number" just to get around the inability to pass a value as a parameter to the search screen seems like a rather drastic limitation. I can do that, but I would rather keep the DB designed the way it originally was. – AgilePro Commented Dec 24, 2014 at 7:53
  • you are totally mixing up a url with db fields. Why would creating slugs make a title less likely to be unique if it was looked up by slug? The url can be made to say /foo-man-chu but the ajax request uses its dewey(sic) decimal system number. remember that you have all the book data available in an object in your scope – charlietfl Commented Dec 24, 2014 at 7:55
 |  Show 3 more comments

4 Answers 4

Reset to default 12

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

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

Relevant github issue: https://github.com/angular/angular.js/issues/10479 Workaround is to encode param twice:

encodeURIComponent(encodeURIComponent("The 3/5 solution"))

when('/catalog/:book', { templateUrl: 'cataloglist.htm', controller: 'catCtrl' })

if your routeparameter book is encrypted it may contain '/' (backslash character), inorder to convert it proper executable in browser you must use encodeURIComponent which converts '/' to '%2F'.

$window.location = '/catalog/'+encodeURIComponent(Solution/The%20Beginning);

发布评论

评论列表(0)

  1. 暂无评论