将空格字符进行URL编码:+还是%20?

944

在URL中,何时会将空格编码为+,何时会编码为%20

6个回答

524

来自维基百科(已添加强调和链接):

当输入到HTML表单中的数据被提交时,表单字段名称和值会被编码并使用GET或POST方法通过HTTP请求消息发送到服务器,或者在历史上通过电子邮件发送。默认情况下使用的编码基于一种非常早期的通用URI百分比编码规则,其中包括若干修改,例如换行符规范化以及将空格替换为“+”而不是“%20”。以这种方式编码的数据的MIME类型是application/x-www-form-urlencoded,并且它目前在HTML和XForms规范中定义(仍然以非常过时的方式定义)。

因此,真正的百分比编码使用%20,而URL中的表单数据则采用修改后的形式,使用+。因此,在?之后的查询字符串中最有可能只看到+


4
所以+编码技术上应该是multipart/form-data编码,而百分号编码则是application/x-www-form-urlencoded? - BC.
25
@BC:不可以 - multipart/form-data 使用 MIME 编码;application/x-www-form-urlencoded 使用 +,而正确编码的 URI 则使用 %20 - McDowell
9
“因此,你很可能只会在查询字符串中的 URL 中看到+号。”这个说法是不够准确的。你永远不应该在 URL 的路径部分中看到“+”号,因为它不会产生你期望的效果(空格)。 - Adam Gent
46
基本上,GET提交的目标是http://www.bing.com/search?q=hello+world,同时存在一个名字中包含空格的资源 http://camera.phor.net/cameralife/folders/2012/2012-06%20Pool%20party/ - William Entriken
18
请注意,对于电子邮件链接,您需要在问号后使用%20而不是+。例如,mailto:support@example.org?subject=I%20need%20help。如果您尝试使用+,则会在电子邮件中出现+es而不是空格。 - Sygmoral
显示剩余6条评论

414

这种混淆是因为URL到今天仍然存在问题。

来自一篇博客文章

以“http://www.google.com”为例。这是一个URL。URL是统一资源定位符,实际上是一个指向网页的指针(在大多数情况下)。自1994年第一次规范以来,URL实际上具有非常明确定义的结构。

我们可以提取关于“http://www.google.com” URL的详细信息:

+---------------+-------------------+
|      Part     |      Data         |
+---------------+-------------------+
|  Scheme       | http              |
|  Host         | www.google.com    |
+---------------+-------------------+

如果我们看一个更复杂的URL,比如:

"https://bob:bobby@www.lunatech.com:8080/file;p=1?q=2#third"

我们可以提取以下信息:

+-------------------+---------------------+
|        Part       |       Data          |
+-------------------+---------------------+
|  Scheme           | https               |
|  User             | bob                 |
|  Password         | bobby               |
|  Host             | www.lunatech.com    |
|  Port             | 8080                |
|  Path             | /file;p=1           |
|  Path parameter   | p=1                 |
|  Query            | q=2                 |
|  Fragment         | third               |
+-------------------+---------------------+

https://bob:bobby@www.lunatech.com:8080/file;p=1?q=2#third
\___/   \_/ \___/ \______________/ \__/\_______/ \_/ \___/
  |      |    |          |          |      | \_/  |    |
Scheme User Password    Host       Port  Path |   | Fragment
        \_____________________________/       | Query
                       |               Path parameter
                   Authority

每个部分的保留字符都不同。

对于HTTP URL,路径片段中的空格必须编码为 "%20"(绝对不是 "+"),而路径片段中的 "+" 字符可以不编码。

现在在查询部分中,空格可以编码为 "+"(出于向后兼容性:不要试图在URI标准中搜索它)或 "%20",而 "+" 字符(由于此模棱两可)必须转义为 "%2B"。

这意味着“blue+light blue”字符串在路径和查询部分必须以不同方式编码:

"http://example.com/blue+light%20blue?blue%2Blight+blue"。

从那里你可以推断出,没有URL结构的语法意识,编码完整构造的URL是不可能的。

总之,你应该在 ? 前面加上 %20,在后面加上 +

来源


2
在问号(?)之前应该有%20,之后应该有加号(+)。抱歉问一个愚蠢的问题。我知道一些关于哈希标签参数是在问号参数之后使用的。虽然它有点不同,因为使用“#”不会重新加载页面。但是我一直在尝试在“#”哈希标签之后使用%20和加号(+),但似乎不起作用。在“#”之后应该使用哪个? - Philcyb
1
@Philcyb 你可能想要阅读这篇文章 https://zh.wikipedia.org/wiki/%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%BC%96%E7%A0%81 - Matas Vaitkevicius
17
实际上,我刚刚看了你给出的LunaTech博客文章,结论似乎更像是:在?之前必须使用%20而不是+,但在?之后只是个人口味问题。拜托了,朋友们,请始终使用基于百分号的编码方式,为更重要的事情腾出一些大脑空间吧。 - nydame
9
哇,老兄。我必须说,ASCII 图表看起来很酷。 - Miłosz Brzechczyn
1
URL并不是“损坏的”。除了URL解码之外,如何解释查询字符串由您决定。如果您想将“+”解释为空格,那也没问题,URL标准并不关心。 - David Ongaro
显示剩余3条评论

30

在URL的“application/x-www-form-urlencoded”内容类型键值对查询部分中,空格只能被编码为“+”。在我看来,这是一个“可能”,而不是一个“必须”。在URL的其余部分,它被编码为%20。

在我看来,最好总是将空格编码为%20,而不是“+”,即使在URL的查询部分中也是如此,因为HTML规范(RFC 1866)指定空格字符应该在“application/x-www-form-urlencoded”内容类型键值对中编码为“+”(请参见第8.2.1条第1款)

这种编码表单数据的方式也在后来的HTML规范中给出。例如,在HTML 4.01规范中查找有关application/x-www-form-urlencoded的相关段落等。

这是一个URL中的示例字符串,HTML规范允许将空格编码为加号:"http://example.com/over/there?name=foo+bar"。因此,在“?”后面,空格可以被替换为加号在其他情况下,空格应该被编码为%20。但由于很难正确确定上下文,最好的做法是永远不要将空格编码为“+”。

我建议对除了RFC 3986第2.3节中定义的“未保留字符”以外的所有字符进行百分号编码。

unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"

实现取决于您选择的编程语言。
如果您的URL包含国际字符,请先将它们编码为UTF-8,然后对结果进行百分比编码。

1
如果请求的资源不是HTML,为什么任何人都应该关心HTML规范呢?我在一些不回应HTML的Web APIs中看到了“+”符号,比如你请求一个pdf文件。我认为他们没有使用“%20”是错误的。 - The incredible Jan
@TheincredibleJan,我同意你的观点。这就是我的回复所涉及的内容。 - Maxim Masiutin
3
当你的回答中说“这是一个可能性,而不是必须”的时候,你指的是哪个规范?我很难找到一个将其列为可能性的规范。在https://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.13.4.1中,在查询部分使用“+”属于该规范的“必须”部分。 - JosephH
2
@JosephH - 感谢您的留言。这是我个人对 MAY 的看法。我已经编辑了帖子。我的意思是,您引用的 HTML 规范定义了“+”,但在 URL 上下文中,其他规则也适用,允许将空格编码为 %20。 - Maxim Masiutin
1
同意!decodeURIComponent('+')返回+。因此,如果一个空格被编码为+,服务器无法将其解码为空格。 - jiajianrong

27
我建议使用%20。你是在硬编码它们吗?尽管如此,在不同的语言中这并不是很一致。如果我没有记错,在PHP中,urlencode()将空格视为+,而Python的urlencode()将其视为%20
编辑:看来我搞错了。Python的urlencode()(至少在2.7.2中)使用quote_plus()而不是quote(),因此将空格编码为“+”。似乎W3C的建议是使用"+",请参见:http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 实际上,您可以在 Python 的问题跟踪器上关注这场有趣的辩论,讨论如何编码空格:http://bugs.python.org/issue13866 编辑#2:我知道将空格编码为“+”是最常见的方法,但只是一个提醒,也许只是我,但我觉得这有点令人困惑:

import urllib
print(urllib.urlencode({' ' : '+ '})

>>> '+=%2B+'

嗨,我也很困惑,当用户提交HTML表单时,表单如何编码空格?使用哪个字符?结果是否取决于浏览器? - Sam YC
1
而在Java中,URLEncoder.encode()方法也会将其转换为+ - рüффп
然后问题就出现了:如何处理POST请求正文中的编码:“Content-Type: application/x-www-form-urlencoded”,其中参数形式为“a=b&c=d”,但根本不在URL中,而是在“文档”的正文中。他们把这个问题搞得一团糟,很难找到明确的答案。 - fyngyrz
Perl的uri_escape()函数将它们视为%20。 - someuser
在Python 3中,方法urllib.parse.urlencode有一个名为quote_via的参数,可以接受一个函数作为值。它的默认值是urllib.parse.quote_plus,但也可以选择其他函数。因此,我们可以使用urllib.parse.quote,从而将空格编码为"%20"。 - wensiso
显示剩余2条评论

17
总结一下这里(有些相互矛盾的)答案,我认为可以概括为:
| standard      | +   | %20 |
|---------------+-----+-----|
| URL           | no  | yes |
| query string  | yes | yes |
| form params   | yes | no  |
| mailto query  | no  | yes |

历史上发生的事情如下:

  1. RFC(请求评论)规范了URL的形式和编码方式。在这种情况下,查询仅是一个“字符串”,没有规定键/值对应该如何编码。
  2. HTTP协议制定了一个关于表单参数中键 / 值对如何编码的标准,并借鉴了URL编码标准,但空格应该被编码为“+”。
  3. Web开发人员说:太好了,我们有了一种编码键 / 值对的方法,让我们把它放到URL查询字符串中。

结果是,在URL的不同部分中,我们有两种不同的编码空格的方式。但这甚至不违反URL标准。从URL的角度来看,“查询”只是一个黑盒子。如果你想使用百分号以外的其他编码方式:自己动手吧。

但正如电子邮件示例所示,从表单参数实现中借鉴URL查询字符串可能会有问题。因此,最终使用%20更加安全,但可能没有现成的库支持。


非常棒的回答,谢谢。但是这部分有点模糊,请问您能解释一下您所说的例子是什么吗?正如“电子邮件示例所示”,从“form-params”实现中借用URL查询字符串可能会有问题。 - aderchox
2
@aderchox 我指的是这条评论:https://dev59.com/I3I-5IYBdhLWcg3w3crS#72833702?noredirect=1#comment45499743_1634293。基本上,电子邮件客户端通常不接受+编码。感谢您的赞扬,但我对自己的答案并不满意,因为其中包含一些不准确之处。+编码并不是“HTTP的人”引入的,而是HTML的人(请参见HTML <form>标签规范)。我计划修正我的答案并提供一些参考资料。 - David Ongaro

0

我很惊讶地发现没有人引用实际的RFC 3986关于“百分号编码”,所以我在这里添加了自己的答案:

由于上述的RFC没有包括将空格编码为+的任何参考,我猜现在使用%20是正确的方式。

例如,"%20"是二进制八位组"00100000"(ABNF:%x20)的百分号编码,它在US-ASCII中对应于空格字符(SP)。


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