浏览器和服务器解决传输中文数据的方法详解
主要内容
浏览器使用HTTP协议传输数据,HTTP协议要求请求的请求行和消息头都是字符串,且使用的文字对应的字符集只能是ISO8859-1.这是一个欧洲的字符集,不支持中文字符。在GET提交表单时可以从URL中的参数看到中文字符对应的形式:
GET /myweb/login?username=%E8%8C%83%E4%BC%A0%E5%A5%87&password=123456
这里的 % 表示这是一个中文字符,百分号后面对应的是一个中文字符对应的16进制数。 服务器获取到后按照以上规则反向编译得到相应字符集对应的中文!
详解
以登录页面为例,当我们在输入框中输入中文后点击登录,浏览器地址栏上可能看到的参数还是中文,但实际传递到服务端时,我们 解析请求行的抽象路径得到参数时,样子如下: /myweb/login?username=%E8%8C%83%E4%BC%A0%E5%A5%87&password=123456 注:我们在用户名输入框里输入的是中文"范传奇"
为什么? 原因:提交表单时,抽象路径会被包含在请求的请求行中。 例如 GET /myweb/login?username=%E8%8C%83%E4%BC%A0%E5%A5%87&password=123456 HTTP/1.1
HTTP协议要求请求的请求行和消息头都是字符串,且使用的文字对应的字符集只能是ISO8859-1.这是一个欧洲的字符集。不支持中文字符。
怎么办? 曲线救国,想办法将不支持的中文用ISO8859-1支持的字符描述并传递给服务端。 首先: ISO8859-1支持数字。 那么我们可以在浏览器中先将中文用UTF-8编码转换为2进制。
GET /myweb/login?username=范&password=123456 HTTP/1.1 不要允许!!!
例如: “范”----UTF-8---->11101000 10001100 10000011(3个字节)
将2进制的1和0表达中文进行传递: GET /myweb/login?username=111010001000110010000011&password=123456 HTTP/1.1 虽然问题得以解决。副作用是:太长!!! 1中文字符就需要24个1或0的字符来表达。
怎么缩短长度? 解决办法:将2进制用16进制表达,仍然可以满足需求。因为16进制可以用字符’0’-‘9’和’A’-‘F’
2进制 10进制 16进制 0000 0 0 0001 1 1 0010 2 2 0011 3 3 0100 4 4 0101 5 5 0110 6 6 0111 7 7 1000 8 8 1001 9 9 1010 10 A 1011 11 B 1100 12 C 1101 13 D 1110 14 E 1111 15 F
换成16进制后的样子: 11101000 10001100 10000011 "范"的UTF-8编码内容 E8 8C 83
GET /myweb/login?username=E88C83&password=123456 HTTP/1.1 原来需要用24个’0’或’1’字符的组合,现在仅需要6个字符就可以表达一个中文了
长度问题尽可能解决了,但是新的问题:如何与实际的英文数组组合区分开呢? 例如: 一个用于注册时取名字就叫:E88C83 此时,提交表单,地址为:GET /myweb/login?username=E88C83&password=123456 HTTP/1.1
服务端接收到该内容时:如何理解:E88C83 是当它为16进制数据,换算为2进制后用UTF-8还原为"范" 还是本来就是E88C83
为了解决混淆问题,URL地址格式要求,如果英文数字组合表达的是16进制内容,则每两位16进制前必须添加一个’%’ 因此,实际传递中文名时应该如下: GET /myweb/login?username=%E8%8C%83&password=123456 HTTP/1.1
如果时本来的英文数字组合应该如下: GET /myweb/login?username=E88C83&password=123456 HTTP/1.1
揣测一个问题.如果改用输入的名字就叫:%E8%8C%83 这里实际上像:‘%’,‘?’,‘=’,&这些字符都属于URL的关键字符。当我们输入该字符传递时,浏览器会将该字符对应的1字节内容也以"%XX"转换。
在服务端进行反向还原时,直接使用java提供的API: URLDecoder.decode()就可以转换了。 decode:解码
代码中我们在HttpServletRequest的parseParameters方法中将参数解码即可