我需要为URI生成href
。当涉及需要百分比编码的保留字符时,例外情况很容易,例如, /some/path;element
的链接应显示为<a href="/some/path%3Belement">
(我知道path;element
代表单个实体)。
最初我正在寻找一个可以做到这一点的Java库,但我最终自己写了一些东西(请看下面的Java失败,因为这个问题不是特定于Java的)。
所以,RFC 3986 does suggest when NOT to encode。当我读到它时,这应该发生在角色属于unreserved (ALPHA / DIGIT / "-" / "." / "_" / "~")
级别时。到现在为止还挺好。但是相反的情况呢? RFC仅提到百分比(%
)总是需要编码。但其他人呢?
问题:假设所有未预留的内容都可以/应该进行百分比编码是正确的吗?例如,左括号(
不一定需要编码,但分号为;
。如果我不对其进行编码,那么在关注/first
时,我最终会查找<a href="/first;second">
*。但是跟随<a href="/first(second">
,我总是按预期最终寻找/first(second
。令我困惑的是,就RFC而言,(
和;
都属于同一个sub-delims
类。正如我想象的那样,对非保留的所有内容进行编码是一个安全的选择,但是当涉及到本地化的URI时,SEOability,用户友好性呢?
现在,Java库失败了。我尝试过这样做
new java.net.URI("http", "site", "/pa;th", null).toASCIISTring()
但这给了http://site/pa;th
这是不好的。观察到类似的结果:
javax.ws.rs.core.UriBuilder
encodePath(String, String)
和encodePathSegment(String, String)
[*] /first
是在点击HttpServletRequest.getServletPath()
<a href="/first;second">
的结果
编辑:我可能需要提一下,这种行为是在Tomcat下观察到的,我已经检查过Tomcat 6和7的行为方式相同。
答案 0 :(得分:3)
假设所有非保留的东西都可以/应该是百分比编码吗?
没有。 RFC 3986说明了这一点:
“在正常情况下,URI中八位字节被百分比编码的唯一时间是在从其组成部分生成URI的过程中。这是一个实现确定哪个保留字符是用作子组件分隔符,可以安全地用作数据。“
这意味着您决定哪个分隔符(即<delimiter>
字符)需要根据上下文进行编码。那些不需要编码的那些不应该被编码。
例如,如果/
出现在路径组件中,则不应对其进行百分比编码,但是当它出现在查询或片段中时,您应对其进行百分比编码。
因此,事实上,;
字符(<reserved>
的成员不应自动进行百分比编码。实际上,java URL和URI类不会这样做;请参阅{ {3}},特别是步骤7),了解<path>
组件的处理方式。
本段加强了这一点:
“保留字符的目的是提供一组与URI中的其他数据可区分的分隔字符。将保留字符替换为其对应的百分比编码八位字节的URI不同。对保留字符进行编码或解码与保留字符对应的百分比编码八位字节的百分比将改变大多数应用程序解释URI的方式。因此,保留集中的字符不受规范化的影响,因此是安全的。由特定于方案和特定于生产者的算法用于分隔URI中的数据子组件。“
因此,这表示包含百分比编码;
的网址与包含原始;
的网址不同。最后一句话暗示它们不应该被自动编码或解码百分比。
这给我们留下了一个问题 - 为什么想要 ;
进行百分比编码?
假设您有一个CMS,人们可以在其中创建具有任意路径的任意页面。稍后,我需要生成到所有页面的href链接,例如站点地图组件。因此,我需要一个算法来知道要逃避哪些字符。在这种情况下,分号必须按字面意思对待,并且应该逃脱。
很抱歉,但并不是说分号应该被转义。
就URL / URI规范而言,;
没有特殊含义。它可能对特定的Web服务器/网站具有特殊意义,但一般(即,没有对网站的具体了解)您无法知道这一点。
如果;
在特定的URI中确实有特殊含义,那么如果你对它进行百分比转义,那么你就打破了这个含义。例如,如果站点使用;
允许会话令牌附加到路径,则百分比编码将阻止它识别会话令牌...
如果;
只是某个客户端提供的数据字符,那么如果您对其进行百分比编码,则可能会更改URI的含义。这是否重要取决于服务器的功能;即,是否作为应用逻辑的一部分进行解码。
这意味着知道“正确的事情”需要深入了解URI对最终用户和/或网站的意义。这需要先进的思维阅读技术来实施。我的建议是让CMS通过适当地转义URI路径之前>>将它们传递给您的软件来解决它。该算法必然将特定于CMS和内容交付平台。它/他们将响应对URL标识的文档的请求,并且需要知道如何解释它们。
(使用任意路径支持任意人有点疯狂。必须有一些限制。例如,甚至Windows都不允许你在文件名组件中使用文件分隔符。所以你是必须在某个地方有一些界限。这只是决定它们应该在哪里的问题。)
答案 1 :(得分:1)
绝对路径部分的ABNF:
path-absolute = "/" [ segment-nz *( "/" segment ) ]
segment = *pchar
segment-nz = 1*pchar
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
pct-encoded = "%" HEXDIG HEXDIG
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
reserved = gen-delims / sub-delims
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
pchar
包含子delim,因此您不必在路径部分中对其中任何一个进行编码::@-._~!$&'()*+,;=
我写了my own URL builder,其中包含路径的编码器 - 一如既往的警告。