前后端数据传输不得不面对的转码问题

by.v_guoyunan 2017-12-11

数据传递转码

数据在传输的过程中,浏览器会对数据进行编码,假如我现在有一条数据 {"name": "测试"},如果我们通过 get 方法传递数据,这条数据会被拼接到 url 请求的后面,如:localhost:8080/src/text.html?name=测试

uri本身是采用ASCII编码的,所以如果是非 ASCII 编码集的字符在传输时都会被编码,编码方法和 encodeURI 的编码规则相同,但是这里的编码规则是由浏览器控制的,不同的浏览器采用的编码方式 (UTF-8,GBK) 不一样,被编码的数据发送给服务器,服务器用 iso-8859-1 编码对数据解码,后端人员通过 request.getParameter("name") 获取参数数据,且获得的数据都是经过解码过的,而解码过程中程序里无法指定,对于 get 请求获得的数据 request.setCharacterEncoding("字符集") 指定解码规则无效。

如果是 post 方法传递数据,浏览器也会对数据进行编码,如果我们在 ajax 请求头里面设置了 setRequestHeader("ContentType","application/x-www-form-urlencoded;charset=UTF-8");
浏览器就会以charset值进行编码,如果没有设置则由网页 meta 标签的 charset 属性决定,被编码过的数据发送给服务器,服务器用 iso-8859-1 编码对数据解码,对于post请求发来的数据后端人员可以使用
request.setCharacterEncoding("字符集") 指定解码规则。

相信你已经找出了乱码的原因,由于 get 方法传的的数据,浏览器的转码规则和服务器的解码规则不一致出现了乱码,我们一般是怎么解决的呢?get 方式发送的数据如果有中文和特殊字符前端会先使用 encodeURI() 方法转码,这样 url 里面的就都是 ASCII 编码集的字符,省去了浏览器的转码,且 encodeURI() 的转码规则可控,受网页 meta 头的 charset 属性影响,

  1. 标签的 charset 属性为 utf-8 时:
var data = '百度&%$#@baidu';
console.log(encodeURI(data));
// %E7%99%BE%E5%BA%A6&%25$#@baidu
console.log(encodeURIComponent(data));
// %E7%99%BE%E5%BA%A6%26%25%24%23%40baidu
  1. 标签的 charset 属性为 GBK 时:
var data = '百度&%$#@baidu';
console.log(encodeURI(data));
// %E9%90%A7%E6%83%A7%E5%AE%B3&%25$#@baidu
console.log(encodeURIComponent(data));
// %E9%90%A7%E6%83%A7%E5%AE%B3%26%25%24%23%40baidu

后端人员获取到用iso-8859-1解码后的数据一般先还原回字节码,然后用前后端协定的方式解码数据,还可以在服务器的配置文件里面进行配置解码规则。而post请求发送的数据可以使用request.setCharacterEncoding(“字符集”)指定解码规则来达到前后端转码统一。

当我们需要传递的数据量大,结构复杂,业务场景,技术实现需要的时候我们就又会发现,乱码的问题依然存在,比如

  • json格式的数据由于特殊字符导致数据解析出现问题,
  • xml格式数据由于特殊字符破坏xml格式导致数据解析出现问题,
  • 前后端一些语言自带的转码方法对一些特殊字符转码结果不一致,以及并非所有特殊字符都会被转码…

如果我们使用 encodeURI 或者 encodeURIComponent 编码传输到后端,后端解码之后的数据总会因为一些特殊字符的转码不一致导致结果不一样,如果再加上 md5 校验之类的,前端传递的数据就会因为 md5 不同无法解析入库。

那么这个时候我们就该考虑有没有一种转码规则可以解决以上所有的问题呢?base64 转码你值得拥有。

base64转码

base64 编码是从二进制到字符的过程,编码受 html 页面头部 mate;标签的 charset 属性影响,charset 属性不同,编码转为二进制时,产生的二进制也是不一样的,所以最终产生的 base64 字符也不一样。

  1. mate 标签的 charset 属性为 utf-8 时:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>base64</title>
</head>
<body>
<script src="base64.min.js"></script>
<script>
var data = '百度&%$#@baidu';
console.log(base64encode(data));
// fqYmJSQjQGJhaWR1
</script>
</body>
  1. mate 标签的 charset 属性为 GBK 时:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="GBK">
<title>base64</title>
</head>
<body>
<script src="base64.min.js"></script>
<script>
var data = '百度&%$#@baidu';
console.log(base64encode(data));
// J+ezJiUkI0BiYWlkdQ==
</script>
</body>

有关 base64 转码原理有兴趣可自行百科。

总结

所以工作中如果涉及到文本框输入等复杂的内容数据传递为了避免中文乱码以及各种特殊符号带来的困扰就使用 base64 转码传递。
如果只是URL里面的传递简单的参数可以使用 encodeURIencodeURIComponent 等转码。

赞一个(10)