您的位置:首页 > 理论基础 > 计算机网络

http request 乱码的真相

2017-03-07 23:45 218 查看
首先,从浏览器端看下有多少种情况:1.在浏览器的地址栏,或者搜索框里输入地址:http://www.test.com/衣服/search?keyword=T恤2.在一个指定了编码的网页中,提交一个form,如:
你好

搜索
当然还有,各种细分的选项,如get/post,form里是否指定了编码。3. ajax请求里的编码。我们从流程上来看,一个http request要经过哪些东东的处理:1.浏览器/JavaScript2.web server,以tomcat/jetty为例3.filter/servlet ,以Java为例4.web 框架,以spring mvc为例。对于在浏览器的地址栏支持输入的地址,各种浏览器是如何处理的,可以参考这个:http://www.ruanyifeng.com/blog/2010/02/url_encoding.html也可以自己简单的测试,在Linux下执行:nc
-l 8080接着在浏览器里直接访问 http://localhost:8080/衣服/search?keyword=T恤,然后在就可以看到nc的输出结果了。当然,浏览器的debug工具也可以很方法地看到编码的结果,不过用nc,就不用自己跑一个web服务器了,非常方便。另外那个keyword=T恤,也是有意选择的,这样可以很方便地看到编码的结果。恤的gbk编码是两个byte,utf-8编码是3个byte,也很容易区别到底是什么编码。简单地总结下对于浏览器地址栏里直接访问:http://www.test.com/衣服/search?keyword=T恤 的编码情况:对于chrome,“衣服”和“T恤”都是utf-8编码;对于IE8,“衣服”和“T恤”都是gbk编码。这里实际上有两个概念,一个是URI的编码,一个是query string(即?后面的字符串)的编码。http request里的Content-Type设置:http request是可以指定request的编码信息的,如:Content-Type: application/x-www-form-urlencoded ; charset=UTF-8但实际上,浏览器都没有这样提供这样的说明……form提交里的编码设置:form可以这样子设置编码:<form
accept-charset="UTF-8" enctype="application/x-www-form-urlencoded;charset=UTF-8"但是实际上浏览器却不一定会这么做……比如,把页页编码设置为gbk,再把form编码设置为utf-8。简单测试,IE8仍然把form编码为gbk,chrome虽然编码为utf-8,但却没有在request里指明。。当然,还有一个小技巧可以强行使用某种编码,那就是我们先自己转换好编码,如:<form id="productSearchForm" action="http://127.0.0.1:8080/%A3%A4"不过,这样意义不大。web
server是如何处理http request的编码的?只讨论tomcat和jetty。Tomcat对于URI的编码,有两个参数可以配置:URIEncoding:这个可以强制指定用什么编码处理URI,默认是ISO-8859-1;useBodyEncodingForURI:这个是一个兼容性比较好的选项,如果在request指定了编码,则采用request里指定的编码。因此,设置了这个选项为true之后,在java代码里就可以调用request.setCharacterEncoding来设置编码了。参考:http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q2Jetty只提供了对query
string的编码的指定方式,没有提供对URI编码的设置。因此,对于http:www.test.com/衣服/abcd/ 这样的URI,jetty总是把“/衣服/abcd/”当做是utf-8编码。参考:http://wiki.eclipse.org/Jetty/Howto/International_Characters#International_characters_in_URLsSpring mvc是如何处理编码的:spring mvc里提供了一个Filter: encodingFilter org.springframework.web.filter.CharacterEncodingFilter
encoding UTF-8 forceEncoding true 到源代码里看一下,可以发现,其实里面只是设置了request的encoding:if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) { request.setCharacterEncoding(this.encoding); if (this.forceEncoding) { response.setCharacterEncoding(this.encoding);
} }但是这个对request URI的编码实际上是不起效的。再看下源代码里是通过j2ee里的request的API来得到的:RequestParamMethodArgumentResolver类里:protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception { if (arg == null) { String[] paramValues
= webRequest.getParameterValues(name); if (paramValues != null) { arg = paramValues.length == 1 ? paramValues[0] : paramValues; } }最终实际上调用的是底层web server的request实现类,如tomcat的是org.apache.catalina.connector.RequestFacade,而web server到底是怎么处理请求的编码的,参照上一小节。http request
编码自动识别这个比较少用到,只有搜索引擎需要识别这种情况。因为搜索引擎需要处理在地址栏里直接输入的字符串的编码。我测试了google, 百度,淘宝的搜索引擎,都能自动识别编码。但是其它的一些非搜索引擎的应用,都不能自动识别编码。当然,程序员通常只保证在自家的网页上,点击的产生的http request能正确地被编码,被识别。那么,假定我们现在要做一个搜索类的功能,而且要能自动识别编码,要怎么处理?以tomcat为例,首先要配置URIEncoding为ISO-8859-1,这样保证信息不丢失。接着,写一个filter,从request里拿到uri,再进行编码识别,转换。编码识别的库参考:https://code.google.com/p/juniversalchardet/还有另外一个思路,写一个nginx的插件,先在nginx层识别,转换好编码。当然原理都是一样的。其它的一些东东:中文域名的的编码:这东东应该没多少人用吧。不过在jetty的网页里看到了一些有用的信息:http://wiki.eclipse.org/Jetty/Howto/International_Characters#International_characters_in_domain_names浏览器实际上会用一个叫Punycode的编码,把域名转换成ascii-only的域名,再发起请求。我测试了下,在chrome里输入:导航.中国实际是转到下面这个域名去了:http://xn--fet810g.xn--fiqs8sC/C++里的编码:我们在源文件test.c里写上:printf(“%s”,
“中文”);那么它在源文件test.c里是什么编码?在编绎出来的test.out/test.exe里是什么编码?运行时输出到屏幕(shell/cmd)上又是什么编码?其实Python也有这种蛋疼的情况……QQ在User-Agent里的信息:用IE8测试时,很神奇地发现在request里发现了QQDownload的字样,真是相当的令人无语。。User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; QQDownload
751)总结:想要实现 www.test.com/王小明/文章 这种url是很困难的,因为你不但要应对各种浏览器的编码,还要处理各种web服务器的不同情况。出现乱码时,首先区分request传过来的是什么编码,然后response返回的是什么编码,再逐一排查。编码问题可以说是程序员无法回避的问题,我相信即使是很有经验的程序员,也会被坑。没有办法,现实世界就是这么坑爹,只能寻根溯源,一一排查了。对于程序员通常,只要保证下面几点就没有问题了:指定网页的编码;配置web server对uri使用request里配置的编码;在ajax请求里先encodeURI();在web
server端对request设置utf-8编码,对于response设置utf-8编码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: