URL编码中的空格问题

本文探讨了URL编码中空格处理的差异,包括RFC1738和RFC3986标准,以及Java、PHP和JavaScript中不同的实现。在HTML表单提交中,空格通常被编码为+,而在其他情况下则编码为%20。此外,高版本Tomcat遵循RFC 3986规范,限制了URL中允许的字符。最后,介绍了UTF-8编码参数的规则,特别是空格编码为%20而不是+。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.这个要看你实现什么标准的URLEncode的了。
1.在1994年订立的RFC1738中。
对字符串中除了-_.三个字符之外的所有非字母数字字符都替换成百分号(%)后跟两位十六进制数。
十六进制数中字母必须为大写。

2.在2005年定义的RFC3986中,将针对- _.~(可见又扩充了一个波浪线字符)四个字符之外的所有非字母数字字符进行百分号编码。
在Java和PHP当中,由于历史原因,导致在进行URLEncode的时候,会将空格编码为+,而不是编码为十六进制编码%20

3.在php当中也提供了标准的RFC1738的实现

在PHP Manual中有对两个函数的说明:

urlencode:返回字符串,此字符串中除了 - _ . 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。此编码与 WWW 表单 POST 数据的编码方式是一样的,同时与 application/x-www-form-urlencoded 的媒体类型编码方式一样。由于历史原因,此编码在将空格编码为加号(+)方面与 RFC1738 编码(参见 rawurlencode())不同。

rawurlencode:返回字符串,此字符串中除了 - _ . 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数。这是在 RFC 1738 中描述的编码,是为了保护原义字符以免其被解释为特殊的 URL 定界符,同时保护 URL 格式以免其被传输媒体(像一些邮件系统)使用字符转换时弄乱。

二.在调查一个错误的时候,偶然发现HTML页面中的部分Link含有+,将tag反编译了一下,发现是因为调用了Java.net.URLEncoder的方法

public static String encode(String s, String enc)

从代码中可以很清晰的看到还特别照顾的将' '转成了'+'。由于印象中的URL编码规则应该是将空格转为%20,就google了一把,结果发现很多人都遇到了这个问题,但基本都是语焉不详。

花了1个多小时仔细搜索了一遍,线索如下:

1、在sun的bug库中找到有人认为该方法不符合RFC2396标准
http://bugs.sun.com/view_bug.do?bug_id=4616184
sun的回答说不是bug,符合HTML4.01标准

为什么不转义-_.*  。URLEncoder注释。

    It appears that both Netscape and Internet Explorer escape  all special characters from this list with the exception  of "-", "_", ".", "*". 
While it is not clear why they are  escaping the other characters, perhaps it is safest to assume that there might be contexts in which the others are unsafe if not escaped. Therefore, we will use the same list. It is also noteworthy that this is consistent with  O'Reilly's "HTML: The Definitive Guide" (page 164).

2、在W3C找到HTML标准的说明
http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4
在这里清楚的看到编码方式是根据ContextType的不同而区别对待的,在form的ContextType是[x-www-form-urlencoded]的时候会对form中的键/值对进行编码,空格被转义成+,其他字符按照[RFC1738]标准处理成%HH的形式。

3、回头再看URLEncoder
发现该类的注释中很明确的写明了:
converting a String to the application/x-www-form-urlencoded MIME format

从以上的结果看来%20似乎只是在使用上的一个误解,因为%20可以被解析成空格,所以就理所当然的认为空格应该被转义为%20。

再进一步,对Java/.Net/JavaScript的相关函数进行下测试,结果发现Java(1.5)与.Net(2.0)的情况一致,但JavaScript的函数还是将空格转换成了%20。看来这个问题的误解完全来自于js的错误……

Java官方的URLEncoder.encode 实际上是为了post请求的content-type为x-www-form-urlencoded来设计的。所以没有什么bug可言。

四.查了一下,是因为高版本tomcat中的新特性:严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了URL中只允许

  • 包含英文字母(a-z,A-Z)
  • 数字(0-9)
  • -_.~4个特殊字符

  以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。

五.编码参数。使用UTF-8字符集按照RFC3986规则编码请求参数和参数取值,编码规则如下:

  • 字符A~Z、a~z、0~9以及字符-_.~不编码。
  • 其它字符编码成%XY的格式,其中XY是字符对应ASCII码的16进制。示例:半角双引号(")对应%22
  • 扩展的UTF-8字符,编码成%XY%ZA…的格式。
  • 空格( )编码成%20,而不是加号(+)。

    该编码方式与application/x-www-form-urlencodedMIME格式编码算法相似,但又有所不同。

    如果您使用的是Java标准库中的java.net.URLEncoder,可以先用标准库中percentEncode编码,随后将编码后的字符中加号(+)替换为%20、星号(*)替换为%2A%7E替换为波浪号(~),即可得到上述规则描述的编码字符串。

    private static final String ENCODING = "UTF-8";
    private static String percentEncode(String value) throws UnsupportedEncodingException {
    return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值