深入分析 web 请求响应中的编码问题(2)解决乱码的关键因素之客户端
- UID
- 1066743
|
深入分析 web 请求响应中的编码问题(2)解决乱码的关键因素之客户端
解决乱码的关键因素之客户端web 页面HTML meta data 中的 charset<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
这里 charset 的作用有二点:
- 通知浏览器应该用什么编码方式解码渲染网页
- 提交表单时浏览器会按其编码数据(post body)发送给服务器。
JSP page 命令中的 charset 与 pageEncoding<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="UTF-8"%>
- contentType 属性里指定的 charset 跟上面提到的 meta data 作用是一样的
- pageEncoding 属性里指定的编码方式是储存该 jsp 文件时所用的编码,eclipse 的文本编辑器可以根据该属性决定储存该文件时采用的编码方式
浏览器通过地址栏发起的 Get 请求针对 get 请求的乱码容易出现在两个地方,一是请求的资源名称,二是查询参数。更复杂的是,不同的浏览器可能使用两种编码分别处理 URL 和查询参数。
表 1 列举了三种常见浏览器的编码情况:
表 1. 三种常见浏览器对地址栏 URL 和查询参数的编码方式浏览器默认 URI 编码默认查询参数编码 英文 Chrome 54 UTF-8 UTF-8 英文 Firefox 51 UTF-8 UTF-8 英文 IE 11 UTF-8 跟随系统 code page
其中 IE 11 中有如下设置:
默认按 UTF-8 发送 URL 路径 开启(设为 false 时按系统 code page 编码 URL 路径)
默认按 UTF-8 为内网 URL 发送查询字符串 关闭默认按 UTF-8 为外网 URL 发送查询字符串 关闭对 URL path 的设定工作正常,对 query string 的设定有 bug,无法生效,参见 以及 。貌似没有办法可以直接实现 IE 地址栏按 UTF-8 编码查询字符串,只好采用如下 workaround:
- 不使用 IE 浏览器(有趣的是从 Firefox 和 Chrome 拷贝出的地址粘贴在 IE 地址栏里时是编好码的)
- 将查询参数放入 URI 内
- 避免直接含有查询参数的地址直接暴露给用户,而是通过 JavaScript encodeuricomponent 后发起请求
在大多数浏览器已默认采用 Unicode 的前提下,URL 中的查询参数不乱码的必要条件是中间件里配置 URI 的解码方式也为 UTF-8(比如 Tomcat 中的 URIEncoding)。
通过表单提交发起的请求对于表单中的参数,无论是通过 get 方法(通过 URL 查询参数)还是 post 方法(通过请求体)提交,浏览器(上节提到的三个浏览器均如此)都会根据页面的 ContentType("text/html; charset=enc")中指定的编码对表单中的数据进行编码,然后发给服务器。在服务器端的程序中我们需要在合适的位置通过 Request.setCharacterEncoding(String enc) 方法设置一致的编码方式,然后才能通过 request.getParameter 获得正确的数据。
表单数据不乱码的前提是页面 content type 中的 charset 和服务器 request 中的 characterEncoding 一致,当然首选都设为"utf-8"。
JavaScriptajaxGet实验发现浏览器对 AjaxGet 请求中 URL 编码的处理与上面讲的对地址栏 URL 的处理略有不同,如表 2 所示:
表 2. 三种常见浏览器对 ajaxGet 的编码方式浏览器默认 URI 编码默认查询参数编码 英文 Chrome 54 UTF-8 跟随页面 content type 英文 Firefox 51 UTF-8 UTF-8 英文 IE 11 UTF-8 跟随系统 code page
这里不同浏览器对查询参数的处理差别较大,所以最好使用 encodeURI 或 encodeURIComponent 手动显式地将整个 URL 或者查询字符串按 UTF-8 编码,详细用法如下:
- encodeURI/decodeURI 将 URI 与其对应的 UTF-8 编码的十六进制形式进行互相转换
该方法不会对 ASCII 字母和数字及部分标点符号进行编码,比如 - _ . ! ~ * ' ( ) 。 另外该方法的目的是对 URI 进行完整的编码,因此对以下在 URI 中具有特殊含义的 ASCII 标点符号不会进行转换。
;/?&=+$,#
- encodeURIComponment/decodeURIComponment encodeURIComponent() 函数
encodeURI() 函数的区别之处在于前者针对的是包含查询字符串的整个 URI,因此未对 URI 中具有特殊含义的 ASCII 标点符号进行转换,而 encodeURIComponent() 函数将转义用于分隔 URI 各个部分的标点符号。
;/?&=+$,#
此外,比较容易与上面四个函数混淆的是 escape/unescape。需要注意的是 escape() 是针对字符串的,而不是针对 URL,其返回的是一个字符的 Unicode 码点的十六进制形式。该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。ECMAScript v3 反对使用该方法,推荐使用 decodeURI() 和 decodeURIComponent() 。
总结来说,escape 函数和编码 URL 没关系,编码 URL 用 encodeURI,编码查询参数的时候用 encodeURIComponent(因为查询参数可能是一个需要编码的 url) 其实现在的主流浏览器已经能对 url 默认按 utf-8 编码,中间件那里设置下 URIEncoding 按 utf-8 解码就能解决 url 编码的问题,所以 encodeURI 不太用到。
反倒是由于 IE 对查询参数的编码还是跟随系统,所以最好使用 encodeURIComponent 将其强制转为 ANSI 表示的 utf-8 编码,这样就不用使用浏览器复杂的编码机制了,可以保证跨浏览器使用。
ajaxPost实验发现上文所述的三个浏览器在提交 ajaxPost 请求时对于 URI 和请求体都是默认按 utf-8 编码,而不受 content type 影响。 所以 ajaxPost 不乱码的必要条件是将服务端 request 中的 characterEncoding 设为"utf-8"。 |
|
|
|
|
|