application/x-www-form-urlencoded
表单的enctype属性表示在发送到服务器之前应该如何对表单数据进行编码,默认值是application/x-www-form-urlencoded。如果表单提交时使用get请求,浏览器或者使用httpClient、okhttp这些组件开发的客户端会把编码后的数据加到URL后面,数据和URL使用“?”分隔。如果表单提交时使用post请求,编码后的数据会被放到请求报文体(body)中提交给服务端
注意,如果是自己使用httpClient、okhttp这些组件开发的客户端,那么设置了Content-Type:application/x-www-form-urlencoded; charset=UTF-8,还需要我们自己把要传的数据编码,组件不会帮我们处理。
编码后的数据是一个字符串,字符串中的键值对用 & 分隔,并且键与值用 = 分隔。如果值包含多字节的字符,那么该字符将被“%HH”取代,即一个百分比符号和两个十六进制数字表示的字符串。HH与字符编码有关,如果 Content-Type 为 application/x-www-form-urlencoded;charset=UTF-8,那么该字符将首先被编码为UTF-8字节数组,再将每个字节转换为HH。
特别要注意,不论字符编码是什么,空格都会被编码为 +,以下三行代码等同于使用application/x-www-form-urlencoded进行编码,都输出 +。
public static void main(String[] args) throws Exception {
System.out.println(URLEncoder.encode(" ", "UTF-8"));
System.out.println(URLEncoder.encode(" ", "GBK"));
System.out.println(URLEncoder.encode(" ", "BIG5"));
}
由于RFC1738没有规定具体的字符编码,所以各浏览器使用的字符编码不一样,可能是UTF-8,也可能是系统字符编码。
encodeURIComponent()
JS有三个常用的数据编码函数。
escape()
这个函数是最古老的,已经不推荐使用。实际上,escape()不能直接用于URL的编码,它的真正作用是返回一个字符的Unicode编码值。比如"春节"的返回结果是%u6625%u8282,也就是说在Unicode字符集中,"春"是第6625个(十六进制)字符,"节"是第8282个(十六进制)字符。
该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。其他所有的字符都会被转义序列替换。在u0000到u00ff之间的符号被转成%xx的形式,其余符号被转成%uxxxx的形式。对应的解码函数是unescape()。所以,"Hello World"的escape()编码就是"Hello%20World"。因为空格的Unicode值是20(十六进制)。
encodeURI()
encodeURI() 函数可把字符串作为 URI 进行编码。该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( ) 。该方法的目的是对 URI 进行完整的编码,因此对于 ;/?:@&=+$,# 这些在 URI 中具有特殊含义的 ASCII 标点符号,encodeURI() 函数是不会进行转义的。
如果值包含多字节的字符,那么该字符将被“%HH”取代,即一个百分比符号和两个十六进制数字表示的字符串,字符编码为UTF-8。
encodeURIComponent()
encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( ) 。其他字符(比如 :;/?:@&=+$,# 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。
请注意 encodeURIComponent() 函数 与 encodeURI() 函数的区别之处,前者假定它的参数是 只是URI 的一部分(比如协议、主机名、路径或查询字符串),因此 encodeURIComponent() 函数将转义用于分隔 URI 各个部分的标点符号。
如果值包含多字节的字符,那么该字符将被“%HH”取代,即一个百分比符号和两个十六进制数字表示的字符串,字符编码为UTF-8。
为什么有时候JS的encodeURIComponent要连续用两次?
一般情况下,前端可以使用JS处理数据,encodeURIComponent(parmeName)+"="+encodeURIComponent(parmeValue),Java服务端接收时, 容器自动解码了, String paramValue = request.getParameter(paramName)。
encodeURIComponent 使用的是 UTF-8 字符编码来编的,如果 request.getParameter(paramName) 时,容器也按 UTF-8 解的话,结果就是正确的,根本无须在客户端进行二次的 encodeURIComponent。但是如果 request.getParameter(paramName)时容器没有按 UTF-8 解吗的话,就会乱码。容器按什么编码来解码,取决于 request.setCharacterEncoding()和servlet容器的配置(如Tomcat的Connector标签的URIEncoding属性)。
可以使用过滤器设置request.setCharacterEncoding("UTF-8"),并且 修改servlet容器配置(有的不用修改,默认就是UTF-8),让容器在解 GET 提交的参数时使用 UTF-8,这样客户端提交前就不用二次编码,服务端接收时也只要直接 request.getParameter(paramName) 即可。
为什么网上会有人提出在客户端对字符串重复编码两次呢?如果因为项目需要,不能指定容器使用何种编码规则来解码提交的参数。比如需要接收来自不同页面、不地编码的参数内容时,这个时候,在客户端对参数进行二次编码,可以有效的避开“提交多字节字符"的这个棘手问题。第一次编码,参数内容便不带有多字节字符了,成了纯粹的 Ascii 字符串。再编码一次,然后提交。接收时容器自动解一次(容器自动解的这一次,不管是按 GBK 、 UTF-8 还是 ISO-8859-1解码,都能够正确的得到结果),然后再在程序中实现一次 解码 (Java中通常使用 java.net.URLDecoder(***, "UTF-8")) 就可以得到提交的参数值。