http request乱码的真相

标签: 基础技术 javaee Request 乱码 | 发表时间:2017-03-02 22:32 | 作者:liuchi1993
出处:http://www.importnew.com

首先,从浏览器端看下有多少种情况:

1.在浏览器的地址栏,或者搜索框里输入地址:http://www.test.com/衣服/search?keyword=T恤

2.在一个指定了编码的网页中,提交一个form,如:

<html>  
<head>  
    <meta charset="gbk">  
</head>  
<body>  
<p>你好</p>  
<form id="productSearchForm" action="http://127.0.0.1:8080/test.html" method="post">  
    <input name="keyword" class="keyword" value="T恤" maxlength="30">  
    <button type="submit">搜索</button>  
</form>  
</body>  
</html>

当然还有,各种细分的选项,如get/post,form里是否指定了编码。

3. ajax请求里的编码。

我们从流程上来看,一个http request要经过哪些东东的处理:

1.浏览器/JavaScript

2.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#Q2

Jetty只提供了对query string的编码的指定方式,没有提供对URI编码的设置。因此,对于http:www.test.com/衣服/abcd/ 这样的URI,jetty总是把“/衣服/abcd/”当做是utf-8编码。

参考: http://wiki.eclipse.org/Jetty/Howto/International_Characters#International_characters_in_URLs

Spring mvc是如何处理编码的:

spring mvc里提供了一个Filter:

<filter>  
    <filter-name>encodingFilter</filter-name>  
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
    <init-param>  
        <param-name>encoding</param-name>  
        <param-value>UTF-8</param-value>  
    </init-param>  
    <init-param>  
        <param-name>forceEncoding</param-name>  
        <param-value>true</param-value>  
    </init-param>  
</filter>

到源代码里看一下,可以发现,其实里面只是设置了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--fiqs8s

C/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编码。

相关文章

相关 [http request 乱码] 推荐:

http request乱码的真相

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

你好

.

登录密码与HTTP Request

- - 博客园_首页
我们知道,在一些主流的浏览器中按F12,就会拉出一个查看web访问详细信息的窗口,在firefox中叫firebug,在chrome或者IE中,则叫developer tools,他们功能都大同小异,当然,比较重要的自然是查看http request与response, 幸运的是,这三个工具都把其放在一个叫Network的tab下面,虽然显示格式略有不同,但基本信息都是一样的.

Struts2获取request三种方法

- - 企业架构 - ITeye博客
Struts2获取request三种方法. struts2里面有三种方法可以获取request,最好使用ServletRequestAware接口通过IOC机制注入Request对象. 在Action中获取request方法一:. 在Action中的代码:. 在JSP页面中获取其中的值:. 方法二:通过ServletActionContext类来获取,使用struts2经验如果处理get传参是中文,只能使用该方法进行处理乱码问题.

关于Pull Request的十个建议

- - 外刊IT评论
Pull Request 是 Bitbucket、 GitHub 等源代码托管系统为了方便开发者之间协作而提供的一个功能,它提供了一个用户友好的 Web 界面来帮助审查人员进行代码审查. 开发人员可以通过 GitHub 发出 Pull Requests 要求请求他人将程序拉下来进行代码审查. 一个好的 Pull Request 不仅仅只是代码的事情,还牵涉到代码审查者对代码的审查,所以开发者不仅要写出好的代码,还必须迎合审查者的审查工作,才能给使得自己贡献的代码顺利通过审查并合并到 master 分支.

HTTP Headers 入门

- johnny - Time Machine
非常感谢 @ytzong 同学在twitter上推荐这篇文章,原文在此. 本文系统的对HTTP Headers进行了简明易懂的阐述,我仅稍作笔记. 什么是HTTP Headers. HTTP是“Hypertext Transfer Protocol”的所写,整个万维网都在使用这种协议,几乎你在浏览器里看到的大部分内容都是通过http协议来传输的,比如这篇文章.

HTTP基础

- - ITeye博客
HTTP的结构主要包括下面几个要点:. HTTP的版本主要有1.0,1.1 和更高版本.    1.1 及以上版本允许在一个TCP连接上传送多个HTTP协议,1.0能 .    1.1 及以上版本多个请求和响应可以重叠,1.0不能.    1.1 增加了很多的请求头和响应头.     一个请求行,若干小心头,以及实体内容,其中的一些消息头和实体内容是可选的,消息头和实体内容需要空行隔开.

HTTP Header 详解

- - 博客园_Ruby's Louvre
HTTP(HyperTextTransferProtocol)即超文本传输协议,目前网页传输的的通用协议. HTTP协议采用了请求/响应模型,浏览器或其他客户端发出请求,服务器给与响应. 就整个网络资源传输而言,包括message-header和message-body两部分. 首先传递message- header,即 http header消息.

fred: 使用 NodeJS + Express 從 GET/POST Request 取值

- - Planet DebianTW
過去無論哪一種網站應用程式的開發語言,初學者教學中第一次會提到的起手式,八九不離十就是 GET/POST Request 的取值. 但是,在 Node.js + Express 的世界中,彷彿人人是高手,天生就會使用,從不曾看到有人撰文說明. 這應該算是開發 Web Service 的入門,在 Client 與 Server 的互動中,瀏覽器發出 GET/POST Request 時會傳值給 Server-side,常見應用就是網頁上以 POST method 送出的表單內容,或是網址列上的 Query Strings (ex: page?page=3&id=5).

[Java][web]利用Spring随时随地获得Request和Session

- - CSDN博客推荐文章
利用Spring随时随地获得Request和Session. 在web.xml中添加  . 1、方法一:通过代码实现. 2、方法二:通过注解实现:. 三、关于RequestContextListener的背景知识:. 基于LocalThread将HTTP request对象绑定到为该请求提供服务的线程上.

HTTP负载测试

- - 博客 - 伯乐在线
英文原文: ON HTTP LOAD TESTING 来源: oschina. 有很多人在谈论HTTP服务器软件的性能测试,也许是因为现在有太多的服务器选择. 这很好,但是我看到有人很多基本相同的问题,使得测试结果的推论值得怀疑. 在日常工作中花费了很多时间在高性能代理缓存和源站性能测试方面之后,这里有我认为比较重要的一些方面来分享.