问题

最近vue项目开发的时候有同事遇到字符串编码的问题,使用axios发送get请求的时候,params传递的参数在后台乱码了,而我本地是正常的,跟踪了一下代码,查找了一些资料,把相关的问题整理了一下

url中查询字符串的编码

后台服务器使用的tomcat,记得以前出现过这类问题,需要修改server.xml中connector标签,添加属性uriEncoding=“UTF-8”,否则tomcat默认以iso-8859-1的字符集解码url中的参数,但是查看idea生成的tomcat配置文件,并没有这个配置,可我这边仍能正常解码:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后想到会不会是tomcat版本变更修改了url查询字符串的默认解码方式,百度了一下,果然tomcat8将url查询字符串的默认解码方式改成了iso-8859-1,而同事那边使用的tomcat7,所以需要配置一下uriEncoding。
然后是另一个问题,tomcat8解码使用utf-8,那前端编码是在什么地方指定的字符集呢?项目是基于开源的若依开发的,查看axios封装类request.js,在拦截器中处理了get请求,使用encodeURIComponent()接口编码了参数:
在这里插入图片描述
这个接口在处理超链接传参或ajax get请求传参时经常用到,但一直没有注意它是如何确定编码字符集的,以前只知道服务器端返回html页面时,content-type响应头中的charset会影响表单提交的编码,w3cschool上查询encodeURIComponent的接口文档,不能通过参数指定编码字符集,那么编码方式似乎是固定的或受浏览器影响,那么encodeURIComponent()的编码方式也受content-type影响吗?百度了一下没有找到结果,遂到MDN查询了一下这个方法,发现这个api的编码方式果然是固定的utf-8
在这里插入图片描述
最后,关于get方式的参数编码还剩一个问题,当从浏览器地址栏输入url和参数时,编码方式是如何确定的?又如何解码?百度了一下,似乎不同的浏览器有不同的编码方式,服务器端大概没有太好的办法处理这种情况,暂时忽略这种情况吧

ajax发送post请求时,请求体中的字符串编码

解决了url中参数编码的问题,自然又想到了post发送请求时,请求体中的内容是如何编码的,以前知道服务器端返回html页面时,content-type响应头中的charset会影响表单提交的编码,浏览器会用同样的字符集编码表单提交的内容,我们这边使用java开发后台,servlet提供Request.setCharacterEncoding接口指定请求体的解码字符集,一般在过滤器或servlet中获取参数前调用一下这个接口,指定字符集就可以了,这个网上说的很多,就不多说了。现在问题是,当使用ajax提交post请求时,上面提到的编码规律仍然适用吗,我修改了一下axios的参数,发现了有意思的现象:
在这里插入图片描述
通过axios修改请求头的content-type,发现无论charset我改成什么,f12观察请求报文,浏览器都显示为utf-8:
在这里插入图片描述
现在有两种解释,一种是axios给我改回了utf-8,另一种则是浏览器强制改回了utf-8,通过跟踪axios源码,排除了第一种情况,axios封装的XMLHttpRequest,跟踪到设置XMLHttpRequest的请求头时,axios并没有改变我设置的utf-8,是原样交给XMLHttpRequest:
在这里插入图片描述
(ps:上面的源码代码也印证了网上说的另一个问题,post请求时若没有传递data参数,用户设置的content-type请求头会被axios删掉)
现在只剩下一种解释,content-type中的charset是浏览器强制改回utf-8的,那么果然如此吗,浏览器为什么强制改回utf-8,只能是utf-8么,我们的系统返回html时,响应头中content-type设置的编码为utf-8,浏览器是受此影响吗?百度了一下,没有找到满意的答案,于是搜索了一下有没有关于ajax技术的官方标准协议,找到了whatwg这个网站,它和w3c是竞争关系,指定一系列的浏览器标准,如html,css,dom,h5接口等,w3c是微软主导的,而whatwg应该是以谷歌为首的一系列浏览器开发商主导的。在whatwg上我找到了答案,先说结论,ajax发送post请求,请求体的内容为普通字符串时,content-type会被浏览器强制修改为utf-8,与返回html时响应头中的content-type编码无关。但由于whatwg是谷歌的,所以ie中是什么情况并不确定,我没有试过。详情见whatwg的官网
在这里插入图片描述
在这里插入图片描述
上面这段主要是浏览器处理content-type的代码逻辑,斜体字如extractedContentType,originalAuthorContentType等应该可以理解为处理方法中的局部变量,this指的应该是XMLHttpRequest的实例对象,this’s xxxx,如request body, author request headers等自然就是对象的成员属性。
步骤4说从ajax实例对象的request header成员属性中取出content-type赋值给局部变量originalAuthorContentType,即originalAuthorContentType的值为用户设置的content-type;
步骤5.1中的Document 应该是指dom中的document对象,USVString应该可以理解为js中的字符串,步骤5整体上应该是说,如果调用XMLHttpRequest的send方法时,如果
用户通过setHeader方法设置过content-type
,且send的参数(即请求体)为document对象或字符串,如果用户设置的content-type编码不是UTF-8,则改成UTF-8;
步骤6大体上是说,如果用户没设置过content-type如何设置content-type,6.1和6.2是请求体为document对象和xml document对象的情况,6.3是剩下的情况,将extractedContentType的值设置为content-type,而这个变量是在4.3中通过 safely extracting设置的,点击safely extracting
在这里插入图片描述
这个safely extracting似乎是当调用send(请求体)时,如何处理请求体的,这里面细分了好多情况,区别不同类型的请求体如何处理,比如blob,formdata等,画红圈的部分scalar value string我百度了一下翻译过来好像和USVString是一个意思,就是js中的字符串(SV就是scalar value),可以看到被强制转化为UTF-8。
至此,大致了解了ajax发送post请求时请求体的编码方式,当请求体为字符串时,编码是强制为UTF-8的。

补充一下axios如何将参数转换成字符串的

以前使用jquery的时候,参数传一个普通js对象,jquery会根据设置的content-type进行转换,如果是x-form-urlencoded,会把js对象转换成字符串’key1=value1&key2=value2…'的形式,当为application-json时,会转换成json字符串;在axios中也会把js对象转换成字符串,但规则和jquery不同,并不依赖用户设置的content-type:在这里插入图片描述
可以看到axios的源码中是如何处理参数的,不同的对象类型,如h5的formdata,blob,URLSearchParams等有不同的处理,最后的红圈中,如果参数是一个普通js对象,会转换成json字符串,最后,如果不是上面判断的任何类型,比如字符串,则原样返回,也就是说,如果要按’key1=value1&key2=value2…'的形式上传参数的话,一种方式是传参时传递’key1=value1&key2=value2…'形式的字符串,也就是网上其他文章提的使用‘qs’将js对象转成字符串;还有一种方式是传递URLSearchParams对象,也就是倒数第二个红圈中,axios会调用其toString方法转换为’key1=value1&key2=value2…'字符串

总结

最后总结一下:
1.url中传参
1.1使用encodeURIComponent对参数编码,字符集是固定的UTF-8,后端tomcat如果是8以下的版本,需要修改server.xml中的uriEncoding参数
1.2当在浏览器地址栏中输入参数时,编码方式依赖浏览器,后端似乎没有什么好的办法处理,暂时忽略
2.post请求体中传参
2.1提交表单的方式,编码字符集是由返回页面时,content-type响应头确定的
2.2ajax提交时,字符串的编码总是UTF-8,是固定的(由于whatwg是谷歌的组织,ie不了解)

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐