RFC3986 - 哪些 pchars 需要进行百分比编码?
Posted
技术标签:
【中文标题】RFC3986 - 哪些 pchars 需要进行百分比编码?【英文标题】:RFC3986 - which pchars need to be percent-encoded? 【发布时间】:2011-08-20 07:04:45 【问题描述】:我需要为 URI 生成一个 href
。除了需要百分比编码的保留字符之外,一切都很容易,例如指向/some/path;element
的链接应显示为<a href="/some/path%3Belement">
(我知道path;element
表示单个实体)。
最初我正在寻找一个 Java 库来执行此操作,但最终我自己编写了一些东西(请查看下面的 Java 失败之处,因为这个问题不是 Java 特定的)。 p>
所以,RFC 3986 does suggest when NOT to encode。当我读到它时,当字符属于unreserved (ALPHA / DIGIT / "-" / "." / "_" / "~")
类时,这应该发生。到目前为止,一切都很好。但是相反的情况呢? RFC 只提到百分比 (%
) 总是需要编码。但是其他人呢?
问题:假设所有不是未保留的内容都可以/应该进行百分比编码是否正确?例如,左括号(
不一定需要编码,但分号;
需要。如果我不对其进行编码,我最终会在关注<a href="/first;second">
时寻找/first
*。但是按照<a href="/first(second">
,我总是最终寻找/first(second
,正如预期的那样。让我感到困惑的是,就 RFC 而言,(
和 ;
都在同一个 sub-delims
类中。正如我想象的那样,对所有非保留的内容进行编码是一个安全的选择,但是在本地化 URI 方面,SEO 能力和用户友好性又如何呢?
现在,Java 库失败了。我试过这样做new java.net.URI("http", "site", "/pa;th", null).toASCIISTring()
但这给了http://site/pa;th
,这是不好的。观察到类似的结果:
javax.ws.rs.core.UriBuilder
Spring's UriUtils - 我已经尝试过 encodePath(String, String)
和 encodePathSegment(String, String)
[*] /first
是点击<a href="/first;second">
时在服务器端调用HttpServletRequest.getServletPath()
的结果
编辑:我可能需要提一下,在 Tomcat 下观察到了这种行为,并且我检查了 Tomcat 6 和 7 的行为方式相同。
【问题讨论】:
【参考方案1】:假设不是未保留的所有内容都可以/应该进行百分比编码是否正确?
没有。 RFC 3986 是这样说的:
"在正常情况下,URI 中的八位字节被百分比编码的唯一时间是在从其组成部分生成 URI 的过程中。这是当实现确定要保留哪些字符时用作子组件分隔符,可以安全地用作数据。"
这意味着您决定需要对哪些分隔符(即<delimiter>
字符)进行编码取决于上下文。不需要编码的就不编码了。
例如,如果 /
出现在路径组件中,则不应对其进行百分比编码,但应在查询或片段中出现时对其进行百分比编码。
所以,事实上,;
字符(它是 <reserved>
的成员不应该自动进行百分比编码。事实上,java URL 和 URI 类不会这样做;具体参见 URI(...) javadoc第 7 步)了解如何处理 <path>
组件。
本段加强了这一点:
“保留字符的目的是提供一组分隔字符,这些字符可与 URI 中的其他数据区分开来。在用相应的百分比编码八位字节替换保留字符方面不同的 URI 是不等价的. 对保留字符进行百分比编码,或对与保留字符相对应的百分比编码八位字节进行解码,将改变大多数应用程序解释 URI 的方式。因此,保留集中的字符受到规范化保护,因此可以安全使用由特定于方案和特定于生产者的算法用于分隔 URI 中的数据子组件。"
也就是说,包含百分比编码;
的 URL 与包含原始 ;
的 URL 不同。最后一句话暗示它们不应该被自动编码或解码。
这给我们留下了一个问题 - 为什么您想要 ;
被百分比编码?
假设您有一个 CMS,人们可以在其中创建具有任意路径的任意页面。稍后,我需要生成指向所有页面的 href 链接,例如站点地图组件。因此,我需要一种算法来知道要转义哪些字符。在这种情况下,分号必须按字面意思处理,并且应该转义。
对不起,分号不应该被转义。
就 URL / URI 规范而言,;
没有特殊含义。它可能对特定的网络服务器/网站具有特殊意义,但一般而言(即没有特定的网站知识)您无法知道这一点。
如果 ;
在特定 URI 中确实具有特殊含义,那么如果您对它进行百分比转义,那么您就破坏了该含义。例如,如果站点使用;
允许将会话令牌附加到路径中,那么百分比编码将阻止它识别会话令牌...
如果;
只是某个客户端提供的数据字符,那么如果对它进行百分比编码,则可能会改变 URI 的含义。这是否重要取决于服务器的工作;即是否解码作为应用程序逻辑的一部分。
这意味着要知道“正确的做法”需要深入了解 URI 对最终用户和/或网站的意义。这需要先进的读心技术来实施。我的建议是让 CMS 通过适当地转义 URI 路径 在 将它们传递给您的软件的任何分隔符来解决它。该算法必然将特定于 CMS 和内容交付平台。它/他们将响应对由 URL 标识的文档的请求,并且需要知道如何解释它们。
(支持任意人使用任意路径有点疯狂。必须有 一些 限制。例如,甚至 Windows 都不允许您在文件名组件中使用文件分隔符。所以你是必须在某个地方有一些界限。这只是决定它们应该在哪里的问题。)
【讨论】:
假设您有一个 CMS,人们可以在其中创建具有任意路径的任意页面。稍后,我需要生成指向所有页面的 href 链接,例如站点地图组件。因此,我需要一种算法来知道要转义哪些字符。在这种情况下,分号必须按字面意思处理,并且应该转义。【参考方案2】: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
包含子分隔符,因此您不必在路径部分中对任何这些进行编码::@-._~!$&'()*+,;=
我写了 my own URL builder,其中包括一个路径编码器 - 一如既往,警告购买者。
【讨论】:
嗯,你是说;
不需要百分比编码吗?一开始我也是这么想的,但似乎是错误的(见我帖子的最后一章)。或者这可能是一个 Tomcat 错误?
@mindas - 我猜 Tomcat 正在遵循旧的 URI 规范 - RFC 2396。在本规范中,;
和 =
保留用于路径段中的参数。一些应用服务器仍然通过 URL 重写 (/foo/bar;jsessionid=***
) 实现这些会话跟踪。如果您决定下载我的库,请参阅示例下的EditPathParams.java
。以上是关于RFC3986 - 哪些 pchars 需要进行百分比编码?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 %(百分比)在 RFC 3986(URI 语法)中不被视为保留字符?