在HTTP查询字符串中必须转义哪些字符?

74

本问题涉及URL查询字符串部分中的字符,这些字符出现在问号?标记字符之后。

根据维基百科的说法,某些字符保持原样,另一些字符使用编码(通常使用%转义序列)。

我一直在试图查找实际规范,以便了解那个维基百科页面中每个项目背后的理由。

矛盾示例1:

HTML规范指定将空格编码为+,其余部分推迟到RFC1738。但是,该RFC表示~不安全,并且“[必须始终在URL中对所有不安全的字符进行编码]”。这似乎与维基百科相矛盾。

在实践中,IE8将~编码为生成的查询字符串,而FF3则保留原样。

矛盾示例2:

维基百科指出,它没有提到的所有字符都必须编码。在维基百科中没有提到!。但是RFC1738表示!是一个“特殊”字符,可以“未编码”使用。这似乎与维基百科所说的必须编码相矛盾。

在实践中,IE8将!编码为生成的查询字符串,而FF3则保留原样。

我明白这背后的道理可能是对于维基百科和规范之间存在疑问的那些字符进行编码。甚至可能进一步编码非[A-Za-z0-9]的所有内容。我只想知道实际标准是什么。

结论

维基百科上介绍的算法准确地对那些不属于RFC3986未保留字符集的字符进行编码。也就是说,它会对除了字母数字和-._~之外的所有字符进行编码。特别地,空格会被编码为+而不是%20,符合RFC3986的规定。

一些应用程序使用较早的RFC。以RFC2396未保留字符集为例,其中包含字母数字和!'()*-._~

相比之下,HTML5工作草案算法将除字母数字和*-._之外的所有字符进行编码。空格的特殊情况仍然被编码为+。值得注意的区别在于,*不会被编码,而~会被编码。(严格来说,这种处理方式与RFC3986兼容,即使*包含在保留字符集中,因为它在查询产生中是允许的子分隔符之一。)


4
维基百科不是标准制定机构。如果有疑问,请使用标准。 - John Saunders
8
@John - 尽管使用正确的标准非常重要,但在此情况下应该使用3986标准,而不是旧的1738标准。 - Anon.
2
每个Web开发人员都必须了解URL编码,特别是在这个上下文中,“保留字符对于每个部分都是不同的”这一节非常相关。 - OfirD
1个回答

63

答案在RFC 3986文件中,特别是第3.4节

查询组件以第一个问号(“?”)字符表示,并以数字符号(“#”)字符或URI的结尾终止。

...

斜杠(“/”)和问号(“?”)字符可以表示查询组件内的数据。

技术上,RFC 3986-3.4将查询组件定义为:

query       = *( pchar / "/" / "?" )

这个语法意味着查询可以包含所有来自pchar以及/?的字符。 pchar指的是路径字符的另一个规范。RFC 3986的附录A列出了相关的ABNF定义,特别是:

query         = *( pchar / "/" / "?" )
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
pct-encoded   = "%" HEXDIG HEXDIG
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

因此,除了所有字母数字和百分号编码字符之外,查询字符串还可以合法地包含以下未编码字符:

/ ? : @ - . _ ~ ! $ & ' ( ) * + , ; =

当然,您可能需要记住,在查询中'='和'&'通常具有特殊意义。


3
注意:除了 =& 之外,服务器端可能会限制其他合法未编码的查询字符串字符,例如在 PHP 中的 .(点号),它将在 $_GET$_POST 中被替换为 _(下划线)。请参见:https://dev59.com/3HVD5IYBdhLWcg3wKYL-(还提供了一种解决方法)。 - GitaarLAB
4
那么,PHP用户需要使用符合规范的解析器来处理 $_SERVER['QUERY_STRING'],而不是依赖于破损的特性如 $_GET - flying sheep
仅澄清一下:这里的“ALPHA”是指英文字母 [A-Za-z]。请参阅 rfc2234 第6.1节 - Lii

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接