这几天都在纠结Java Web开发中的中文编码问题。其实,很多Java Web开发者都被中文编码“折磨”过,网络上有大量的讨论。以前我也读过这方面的博文,读完后感觉似乎懂了,好像知道了编码问题的原因和解决方法。但是, 一旦投入到实际开发中,发现自己其实没懂,囧!
连续纠结了几天,总算对前因后果有个清晰地认识,故“略谈”一下。之所以略谈,是因为我并非(也没有能力)完整地阐述Java Web开发的中文编码问题,而是就事论事地总结这几天遇到的问题和收获。
问题
使用HttpClient 3.x发送GET或POST请求,请求参数中包含中文。服务器是Tocmat 5.5,通过断点调试,发现Servlet拿到的中文参数是乱码。显然,HttpClient和Tomcat没有就中文参数的编码达成一致。
于是,开始深入HttpClient和Tomcat的代码,结合断点调试,发现中文编码问题并不是想象中的那么简单。
术语约定
为了使得描述更加请求,我对本文中出现的“术语”进行约定,避免一词多义引起的歧义。
- Encoding: 编码(动词)
- Decoding: 解码(动词)
- Charset: 编码或解码使用的字符集
另外,编码了的数据必然需要解码,因此encoding和decoding往往是同现的。不过为了叙述简练,下文需要两者同现的地方,仅使用encoding。
哪些数据需要encoding?
在研究中文编码问题前,我们首先要弄清一个问题:哪些数据需要encoding?
一个Http请求的数据大致包括URI、Header、和Body三个部分。这三个部分貌似都需要encoding,不过我这次只涉及到URI和Body,因此 就不讨论Header了。
我们一般关心请求参数的中文编码问题。虽然URI Path中也可以包括中文,但是。。。这不是给自己找麻烦吗?
GET的请求参数在QueryString中,是URI的一部分。因此,对于GET请求,我们需要关注,URI是如何encoding的?
POST的请求参数在Body中,因此,对于POST请求,我们则需要关注,Body是如何encoding的?
对于HttpClient和Tomcat来说,encoding和decoding本身是很容易的事情,关键是要知道charset是什么?要不通过API进行设置,要不通过配置文件进行配置。麻烦的是,URI和Body的charset还可以不一样,使用不同的方法进行设置和配置。
HttpClient是一个类库,通过自身提供的API对URI和Body的charset进行设置;Tomcat通过配置项和Servlet API,对URI和Body的charset进行设置。
HttpClient如何设置charset?
我们先看看如何设置GET请求QueryString的charset,然后看看POST请求Body的charset,最后看看如何获取响应数据的charset。
设置GET请求QueryString的charset
我们通过GETMethod的setQueryString方法设置QueryString。setQueryString方法有两种原型,我们分别看看。
原型一以参数键值对的形式设置QueryString,使用固定的UTF-8作为charset,而且做URLEncode。因此,调用原型一之后,HttpClient就不会对QueryString再做任何encoding了。
如果不想使用UTF-8,那么可以使用原型二。
原型二直接设置QueryString的内容。需要注意的是,queryString参数一定是按照某种charset进行URLEncode之后的字符串
。
另外,也可以通过GETMethod的构造函数,直接设置URLEncode之后的uri
(包括了QueryString):
设置POST请求Body的charset
首先,我们可以在POST请求中的Header中设置Content-Type:
在这里,Body的charset就UTF-8。
其次,如果没有设置Content-Type,我们还可以设置HttpClientParam的ContentCharset:
然后,如果没有设置HttpMethodParams的ContentCharset,我们还可以设置HttpMethodParams的ContentCharset:
这三种设置方法的优先级依次递增,也就是说如果同时设置,则以后面的为准。如果都没有设置,默认charset是ISO-8859-1。
响应数据的charset
我们一般使用HttpMethodBase(GETMethod和PostMethod的父类)的getResponseBody系列方法获取响应数据。getResponseBody系列方法包括:
我比较喜欢getResponseBodyAsString方法,因为返回值类型是String,直接可以使用。不过,提到String就必须想到charset
。响应数据的charset肯定由Web Server(Tomcat)设置的,HttpMethodBase是怎么知道的呢?
我们看看getResponseBodyAsString()方法的代码:
顾名思义,getResponseCharSet方法的功能就是获取响应数据的charset。那就看看她的代码吧:
可见,getResponseCharSet方法Content-Type Header获取响应数据的charset。这要求Servlet必须正确设置response的Content-Type Header
。
Tomcat如何设置charset?
即使HttpClient正确设置了charset,Tomcat还要知道charset是什么,才能正确decoding。我们先看看如何设置GET请求QueryString的charset,然后看看POST请求Body的charset,最后看看Servlet响应数据的charset。
设置GET请求QueryString的charset
Tomcat通过URI的charset来设置QueryString的charset。我们可以在Tomcat根目录下conf/server.xml
中进行配置。
URIEncoding属性就是URI的charset,上述配置表示 Tomcat认为URI的charset就是UTF-8。如果HttpClient也使用UTF-8作为QueryString的charset,那么 Tomcat就可以正确decoding。详情可以参考org.apache.tomcat.util.http.Parameters类的handleQueryParameters的方法:
Tomcat在启动的过程中,如果从conf/server.xml中读取到URIEncoding属性,就会设置queryStringEncoding的值。当Tomcat处理HTTP请求时,上述方法就会被调用。
默认的server.xml是没有配置URIEncoding属性的,需要我们手动设置
。如果没有设置,Tomcat就会采用一种称为“fast conversion”的方式解析QueryString。详情可以参考org.apache.tomcat.util.http.Parameter类的urlDecode方法。
useBodyEncodingForURI是与URI charset相关的另一个属性。如果该属性的值为true,则Tomcat将使用Body的charset作为URI的charset。下一节将介绍Tomcat如何设置Body的charset。如果Tomcat没有设置Body的charset,那么将使用HTTP请求Content-Type Header中的charset。如果HTTP请求中没有设置Content-Type Header,则使用ISO-8859-1作为默认charset。详情参见org.apache.catalina.connector.Request的parseParmeters方法:
默认的server.xml是没有配置useBodyEncodingForURI属性的,需要我们手动设置
。如果没有设置,Tomcat则认为其值为false。需要注意的是,如果URIEncoding和useBodyEncodingForURI同时设置,而且Body的charset已经设置,那么将以Body的charset为准
。
设置POST请求Body的charset
设置Body charset的方法很简单,只要调用javax.servlet.ServletRequest接口的setCharacterEncoding方法即可,比如request.setCharacterEncoding("UTF-8")。需要注意的是,该方法必须在读取任何请求参数之前调用,才有效果。详情可以参见该方法的注释:
也就是说,我们只有在调用getParameter或getReader方法之前,调用setsetCharacterEncoding方法,设置的charset才能奏效。
响应数据的charset
设置响应数据charset的方法很简单,只要调用javax.servlet.ServletResponse接口的setContentType方法即可,比如response.setContentType("text/html;charset=UTF-8")。需要注意的是,该方法的调用时机也是有讲究的,详情可以参见该方法的注释:
如果Servlet正确设置了响应数据的charset,那么HTTP响应数据中就会包含Content-Type Header。HttpClient的getResponseBodyAsString方法就可以正确decoding响应数据。
总结
在开发Java Web应用的过程中,遇到中文乱码问题,应该是一件正常的事情。我们不必首先怀疑HttpClient或Tomcat有莫名奇妙的bug,往往都是我们使用不当或配置不当。凡事总有原因,总要相信科学嘛!如果想彻底了解中文编码的前因后果,我觉得可以从HTTP规范、Servlet规范、HttpClient的API文档和Tomcat的配置文档入手,必要时可以追踪HttpClient和Tomcat的代码。
分享到:
相关推荐
- **commons-codec-1.3**:这个文件提供了编码和解码的工具,对HttpClient发送和接收的数据进行处理。 **知识点详解:** 1. **HTTP客户端接口**:HttpClient提供了一个灵活的、可扩展的接口,可以定制请求头、请求...
在Android客户端向服务器提交请求时,遇到中文乱码问题是一个常见的困扰。...在本例中,使用`URLEncoder`和`URLDecoder`进行编码和解码,有效地解决了Android客户端与Tomcat服务器之间的中文乱码问题。
**模拟Tomcat工作原理** Tomcat是一款开源的Java Servlet容器,它是Apache软件基金会下的Jakarta...通过阅读和分析这些代码,我们可以更深入地学习Tomcat的工作原理,这对于理解和开发Web应用服务器具有很大的价值。
【标题】:“Tomcat异常”涉及的知识点 在IT领域,Tomcat是一个广泛使用的...综上所述,解决“Tomcat异常”需要从日志分析、配置检查、依赖管理、代码调试等多个方面入手,结合使用各种工具和技巧,以找出并修复问题。
本文将详细介绍在Apache Tomcat服务器上设置GZIP压缩的原理和配置方法。 首先,让我们探讨一下GZIP压缩的原理。GZIP压缩算法基于deflate算法,是一种文件压缩格式,广泛用于在HTTP协议中进行文件压缩。当服务器对...
如果响应内容显示为乱码或`ContentLength`为-1,说明Tomcat成功地对响应内容进行了Gzip压缩。 这可以通过编写一段Java测试代码实现,该代码使用了Apache Commons HttpClient库来发送带有`Accept-Encoding: gzip, ...
Tomcat服务器的gzip压缩功能是一种优化策略,旨在提高Web应用的性能,减少网络传输的数据量。HTTP压缩主要通过将服务器发送到客户端的资源(如HTML、CSS、JavaScript等)压缩后再传输,从而加快页面加载速度,减少...
3. commons-codec-1.9.jar:Apache Commons Codec库的版本1.9,包含各种编码和解码算法,如URL编码、Base64编码等,这些在处理数据时可能会用到。 4. commons-logging-1.2.jar:Apache Commons Logging库的版本1.2,...
同时,使用 Commons Logging 可以灵活地调整日志输出,便于问题排查和性能分析。 标签"client"表明这些库主要面向客户端应用,通常与服务器端的HTTP服务器组件(如Apache Tomcat、Jetty等)相对,用于实现客户端...
此外,还会学习如何处理JSP中文乱码问题,使用JSP标签和JavaBean。 5. **JavaWEB高级开发技术**:包括Servlet过滤器和监听器的使用,以及JSP标签和行为元素。此外,课程还将讲解WebService、云计算、工作流、搜索...
5. **部署应用**:将Servlet打包到WAR文件中,部署到支持Java EE的Web服务器(如Tomcat)上。 6. **异步处理**:Servlet 3.0及以上版本支持异步处理,可以提高性能,避免阻塞线程。 在实际开发中,为了更好的性能...
使用JUnit和Mockito等工具可以进行单元测试,而Apache HttpClient或cURL可用于集成测试和客户端模拟。 7. **服务治理**: ESB(企业服务总线)和SOA(面向服务的架构)框架如Apache CXF、WSO2和IBM WebSphere ESB...
Apache Commons是一个非常有用的工具包,解决各种实际的通用问题。(附件中提供了该工具包的jar包,及源文件以供研究) BeanUtils Commons-BeanUtils 提供对 Java 反射和自省API的包装 Betwixt Betwixt提供将 ...
Java开发项目管理是一个涵盖多个方面的主题,涉及到软件开发的整个生命周期,包括需求分析、设计、编码、测试和维护。在Java环境下,一个完整的项目管理流程通常会涉及一系列技术和工具的运用,以确保项目的高效和...
面向服务架构(Service-Oriented Architecture,简称SOA)是一种设计思路和技术框架,它将应用程序的不同功能单元(称为服务)通过统一的服务标准和协议进行封装,这些服务可以通过网络进行通信与交互,从而实现业务...
- **技术栈**:采用Springmvc、Spring、Mybatis、EasyUI、Maven、Svn、MySql、Redis、Tomcat、Nginx、HttpClient、Solr和RabbitMQ。 - **项目描述**:为化妆品销售开发的网上购物平台,包含商品管理、订单管理、用户...
在传输层上压缩WebService的请求和响应是提高网络通信效率的一种常见策略,特别是在处理大量数据交换或带宽有限的环境中。这一技术旨在减小通过网络传输的数据量,从而加快数据传输速度,降低服务器负载,并节省网络...
这些功能旨在解决师生在校园生活中遇到的各种问题,如快速熟悉校园环境、提供失物招领平台、二手交易市场以及实时更新的公告通知。 在设计过程中,项目采用了C/S(客户端/服务器)架构,客户端基于Android ...
在IT行业中,Web Service是一种基于开放标准(如XML、SOAP、WSDL和UDDI)的通信协议,用于让不同的应用程序之间进行交互。本实践指南《实战 Web Service 压缩传输》着重探讨了在传输大量数据时如何利用压缩技术来...