URL之get、post 乱码处理

URL之get、post 乱码处理

Web 开发常见 get、post 乱码问题。

只要保证 Client 和 Server 各自使用的字符集在编码、解码时一致即可。


URL

URL 技术简单成一个字符串(实际上 URL 的结构非常复杂,只是我们使用的简单而已)。
更多可参考 每个 Web 开发者都应该知道的关于 URL 编码的知识

举例:http://bob:bobby@www.oschina.net/file?q=2#100

PartDataServer API
Schemehttp/httpsrequest.getScheme()
userbob
passbobby
host addresswww.oschina.netrequest.getServerName()
port80/443request.getServerPort()
path/filerequest.getServletPath()
query parametersq=2request.getQueryString()、request.getAttribute()
fragement100

path 是应用路径部分,是 URL 去掉协议、域名、端口和查询参数剩下的部分。
query parameters 是 path 后紧跟的 ? 后面的部分,用 & 连接各个查询参数。

path 和 query parameters 不一样,有些浏览器对查询參数的编码和path部分的编码是不一致的。

fragment 信息片断。字符串,用于指定网络资源中的片断。例如一个网页中有多个名词解释,可使用fragment直接定位到某一名词解释(也称为锚点)。

详细参考关于 URL 编码
依据上面的文章总结的规律(这个文章较老了,有些规律可能已经不用了,但也说明了一定问题):

  • path 部分或者说除查询参数外的 URL 部分,各浏览器用UTF-8编码;
  • 查询參数,各浏览器依据操作系统编码决定;

Tomcat

一般,请求都是先发给Web容器(以 Tomcat 为例),URL 会被 Web 容器解码。
Tomcat 容器在config/server.xml的 connector 标签中设定解码配置。

Tomcat如何解码

  • Tomcat 8 以下(默认ISO-8859-1
    默认配置:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
  • Tomcat 8 及以上(默认UTF-8
    解码方式依赖 connector 标签的”strict servlet compliance”值,默认 off 时使用 UTF-8 解码,如果打开的话,使用 ISO-8859-1 解码。

解决

  • 方案一:指定 Tomcat 解码方式(如UTF-8)
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"  URIEncoding="utf-8"  />
  • 方案二:使用Http Header中指定的charset解码
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"  useBodyEncodingForURI="true"  />
使用http header中指定charset进行decode(例如:Content-Type: charset=UTF-8)。
指定方法:
Html4 页面: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Html5 页面: <meta charset="UTF-8">

Controller层: request.setCharacterEncoding("UTF-8");
  • 方案三(建议):手动转化
    如果 Tomcat 容器里不止一个应用时,我们可以编码设置。
String _keys = request.getParameter("keys") != null ? request.getParameter("keys").toString() : "";
String keys = new String(_keys.getBytes("ISO8859-1", "UTF-8"));

总结:对于上面三种解决方法,要保证 Web 容器对 URL 的编码是用的 ISO8859-1,如果是人为因素或容器配置因素等造成的其他编码则不适用。

定位浏览器对 query parameters 的编码方式

  • 调试工具
    利用调试工具找到 URL 里中文对应的位置(如%E7%BC%96%E7%A0%81)。
    方法一:使用工具 Atool站长工具等工具解码出对应的中文是“编码”。
    方法二:去掉%得到6个十六进制数,对比 Unicode 码表看出来是“编码”。
    不要通过浏览器的地址栏看URL编码,很多浏览器的地址栏会对 URL 解码显示。

使用与编码一致的解码方式

  • 找出浏览器对 URL 的编码方式;
  • 使用一致的解码方式解码;
    【解决】里的三种方法任选其一。

扩展

编码:ISO8859-1(别名Latin1,有时写Latin-1)

单字节编码,其编码范围(0x00-0xFF)使用了单字节内的所有空间。向下兼容ASCII(0x00-0x7F 之间与 ASCII 完全一致,0x80-0x9F 之间是控制字符,0xA0-0xFF 之间是文字符号。ASCII 编码是一个 7 位的容器,ISO-8859-1 编码是一个 8 位的容器。)

正因为 ISO-8859-1 编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。这是个很重要的特性,MySQL数据库默认编码是Latin1就是利用了这个特性。

ISO-8859-1 收录的字符除 ASCII 收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号出现的比较晚,没有被收录在 ISO-8859-1 当中。

HTML 4.01 支持 ISO 8859-1 (Latin-1) 字符集。
ISO-8859-1 的较低部分(从 1 到 127 之间的代码)是最初的 7 bit ASCII。
ISO-8859-1 的较高部分(从 160 到 255 之间的代码)全都有实体名称的(实体名称对大小写敏感)。

JavaScript 编码解码的三种方式

  • escapeunescape
    字符串编码(区别与另外两种对 URL 编码的函数),作用是让字符串能在所有电脑上可读。
  • encodeURIdecodeURI
    Javascript 中真正用来对 URL 编码的函数。
  • encodeURIComponentdecodeURIComponent
    URL 的组成部分进行个别编码。常用于对 URL 参数所有部分进行编码。

encodeURI 和 encodeURIComponent 的比较
encodeURI 和 encodeURIComponent 都是 ECMA-262 标准中定义的函数,所有兼容这个标准的语言(如JavaScript, ActionScript)都会实现这两个函数。它们都是用来对URI (RFC-2396)字符串进行编码的全局函数,但是它们的处理方式和使用场景有所不同。为了解释它们的不同,我们首先需要理解RFC-2396中对于URI中的字符分类:

  1. 保留字符(reserved characters):URI 中的保留关键字符,用于分割 URI 各部分。
    “;” | “/” | “?” | “:” | “@” | “&” | “=” | “+” | “$” | “,”
  2. Mark字符(mark characters):在RFC-2396中特别定义,但没有特别说明用途,可能是和别的RFC标准相关。
    “-” | “_” | “.” | “!” | “~” | “*” | “’” | “(” | “)”
  3. 基本字符(alphanum characters):URI 中的主体部分,包括所有的大写字母、小写字母和数字。

encodeURI:该函数对传入字符串中的所有非(基本字符、Mark字符和保留字符)进行转义编码。所有的需要转义的字符都按照UTF-8编码转化成为一个、两个或者三个字节的十六进制转义字符(%xx)。例如,字符空格”“转换成为”%20”。在这种编码模式下面,需要编码的ASCII字符用一个字节转义字符代替,在\u0080和\u007ff之间的字符用两个字节转义字符代替,其他16为Unicode字符用三个字节转义字符代替。
encodeURIComponent: 该函数处理方式和encodeURI只有一个不同点,那就是对于保留字符同样做转义编码。例如,字符”:”被转义字符”%3A”代替。

CSDN 网站搜索“学习”效果对比:

escape("http://so.csdn.net/so/search/s.do?q=学习");
"http%3A//so.csdn.net/so/search/s.do%3Fq%3D%u5B66%u4E60"
encodeURI("http://so.csdn.net/so/search/s.do?q=学习")
"http://so.csdn.net/so/search/s.do?q=%E5%AD%A6%E4%B9%A0"
encodeURIComponent("http://so.csdn.net/so/search/s.do?q=学习")
"http%3A%2F%2Fso.csdn.net%2Fso%2Fsearch%2Fs.do%3Fq%3D%E5%AD%A6%E4%B9%A0"

之所以有这两个不同函数,完全是因为 JS 代码对 URI 的两种不同编码需求。
如“http://www.mysite.com/send-to-friend.aspx?url=http://www.mysite.com/product.html
在这个 URI 字符串中。send-to-friend.aspx 页面会创建 HTML 格式的邮件内容,里面会包含一个链接,这个链接的地址就是上面 URI 字符串中的url值。显然上面的 url 值是 URI 中的一个部分,里面包含了 URI 保留关键字符。我们必须调用 encodeURIComponent对它进行编码后使用,否则上面的 URI 字符串会被浏览器认为是一个无效的 URI 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值