AngularJS路由参数包含任何字符

时间:2014-12-24 07:29:22

标签: javascript angularjs url-routing url-encoding

我是AngularJS的新手,请原谅我,如果这是显而易见的,但我正在寻找能够回答这个棘手问题的人。我正在实现一个应用程序,需要将一些参数传递给特定视图以显示有关书籍的详细信息。基本上我希望能够使用以下路由表达式:

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',
        });
    }]);

表达式/book/:title/:chapter应该允许我传入书名的名称。我希望能够通过任何一本书的任何标题。为了确保正确分隔内容,我将对标题值进行URL编码,以便在编码值中没有斜杠,并且该值将由斜杠字符清楚地分隔。这是构造包含值的URL的常规方法。

问题是存在包含斜杠字符的书名(例如The 3/5 solution)这是以The+3%2F5+Solution编码的URL。所以可以构建这样的URL:

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

然而,我的经验似乎表明,在将其分解为参数之前,整个值都是URL解码的!这意味着任何带有斜杠的数据值将被误解释为两个值,并且路径参数的模式匹配被破坏,并且只传递值的前半部分。 ,章节中也可能有一个斜线。

如果我正在制作REST服务,我会对该值进行URL编码,并且在每个片段被解码之前,URL将被解析为片段。例如,我可以在URL中使用查询参数:

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

这将正常工作。使用URL编码,我可以在标题中传递任何字符串值。我本来希望路由参数可以做同样的事情......但我已经提到过我对AngularJS很新。

在确定碎片看起来像一个非常严重的错误之前,将%2F解码为斜线。显然,您根本无法将带斜杠的值作为路由参数传递。 我在这里遗漏了什么吗?有什么方法可以让我安全地传递一个带有任何可能角色的书名(以及带有任意字符的章节标题)作为路线参数?

4 个答案:

答案 0 :(得分:11)

看一下棱角分明的route.js sourcecode,就有可能实现你想要的东西:

  

path可以包含以冒号开头并以星号结尾的命名组:   e.g。:name*。当路线匹配时,所有字符都热切地存储在给定$routeParams下的name中。   

例如,/color/:color/largecode/:largecode*\/edit之类的路线会匹配   /color/brown/largecode/code/with/slashes/edit并提取:   
color: brown   
largecode: code/with/slashes

注意示例中:largecode*\ param末尾的反斜杠。它不存在于以星号结尾的命名组的描述中,但它在示例中。我没有对此进行测试,也不知道是否需要反斜杠,因此请考虑它可能/可能不需要。

因此,您的问题的解决方案将如下所示: /book/:title*/chapter/:chapter*

注意添加的/chapter/部分。为了区分两个命名组,需要它。如果您只使用/book/:title*/:chapter*,则所有内容都属于:title*命名组。但是,如果您使用/:title*/chapter/:chapter*,则angular知道:title*命名组何时结束:何时在路由中遇到/chapter//chapter/之后的所有内容都属于:chapter*命名组

答案 1 :(得分:3)

由于上述行为,似乎无法使用URL编码。这意味着您必须使用代表完整Unicode字符集的编码,而不使用斜杠或百分号或加号,也不得使用任何其他可能导致URL解码器认为存在要解码的编码值的编码。

该值的BASE 64编码将起到作用,解决方案似乎是定义以下过滤器:

    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)));
        }
    });

这允许您编写如下代码:

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

然后,在接收方,您调用btoa将值重新用于您想要的任何目的。然后将值The 3/5 solution编码为VGhlJTI1MjAzJTJGNSUyNTIwc29sdXRpb24=,它始终是路由参数模式识别器中的单个参数,并且会被精确地解码回已发送的值。

完全支持语言。一本名为奥の細道的书籍将被编码为JTI1dTU5NjUlMjV1MzA2RSUyNXU3RDMwJTI1dTkwNTM= - 再次避免任何可能破坏该值的问题。

额外的encodeURIComponentescape调用是为了欺骗JS将unicode字符串转换为UTF-8编码,然后由window.atob函数进行转换,否则会失败某些高阶Unicode字符。有关更多信息,请访问Mozilla Developer Network

答案 2 :(得分:0)

when(&#39; / catalog /:book&#39;,{             templateUrl:&#39; cataloglist.htm&#39;,             控制器:&#39; catCtrl&#39;         })

如果您的路线参数书已加密,则可能包含&#39; /&#39; (反斜杠字符),为了在浏览器中转换正确的可执行文件,你必须使用encodeURIComponent来转换&#39; /&#39;到&#39;%2F&#39;。

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

答案 3 :(得分:0)

相关的github问题:https://github.com/angular/angular.js/issues/10479 解决方法是对参数进行两次编码:

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