httpclient编码

2024-06-21 00:58
文章标签 编码 httpclient

本文主要是介绍httpclient编码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题 

    使用HttpClient 3.x发送GET或POST请求,请求参数中包含中文。服务器是Tocmat 5.5,通过断点调试,发现Servlet拿到的中文参数是乱码。显然,HttpClient和Tomcat没有就中文参数的编码达成一致。
    于是,开始深入HttpClient和Tomcat的代码,结合断点调试,发现中文编码问题并不是想象中的那么简单。

术语约定 

    为了使得描述更加请求,我对本文中出现的“术语”进行约定,避免一词多义引起的歧义。

  1. Encoding: 编码(动词)
  2. Decoding: 解码(动词)
  3. 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方法有两种原型,我们分别看看。   

[java]  view plain copy
  1. public void setQueryString(NameValuePair[] params){  
  2.     LOG.trace("enter HttpMethodBase.setQueryString(NameValuePair[])");  
  3.     queryString = EncodingUtil.formUrlEncode(params, "UTF-8");  
  4. }  


    原型一以参数键值对的形式设置QueryString,使用固定的UTF-8作为charset,而且做URLEncode。因此,调用原型一之后,HttpClient就不会对QueryString再做任何encoding了。

    如果不想使用UTF-8,那么可以使用原型二。

   

[java]  view plain copy
  1. public void setQueryString(String queryString){  
  2.      this.queryString = queryString;  
  3. }  
 
    原型二直接设置QueryString的内容。需要注意的是,queryString参数一定是 按照某种charset进行URLEncode之后的字符串  

    另外,也可以通过GETMethod的构造函数,直接设置 URLEncode之后的uri   (包括了QueryString):

   

[java]  view plain copy
  1. public GetMethod(String uri) {  
  2.     super(uri);  
  3.     LOG.trace("enter GetMethod(String)");  
  4.     setFollowRedirects(true);  
  5. }  


    设置POST请求Body的charset
 

    首先,我们可以在POST请求中的Header中设置Content-Type:

   

[java]  view plain copy
  1. PostMethod method = new PostMethod();  
  2. method.addRequestHeader("Content-Type","text/html;charset=UTF-8");  


    在这里,Body的charset就UTF-8。

    其次,如果没有设置Content-Type,我们还可以设置HttpClientParam的ContentCharset:
    
[java]  view plain copy
  1. HttpClient httpClient = new HttpClient();  
  2. HttpClientParam params = httpClient.getParams();  
  3. params.setContentCharset("UTF-8");  
   
    然后,如果没有设置HttpMethodParams的ContentCharset,我们还可以设置HttpMethodParams的ContentCharset:
   
[java]  view plain copy
  1. PostMethod method = new PostMethod();  
  2. HttpMethodParams params = method.getParams();  
  3. params.setContentCharset("UTF-8");  

    这三种设置方法的优先级依次递增,也就是说如果同时设置,则以后面的为准。如果都没有设置,默认charset是ISO-8859-1。

      响应数据的charset  

    我们一般使用HttpMethodBase(GETMethod和PostMethod的父类)的getResponseBody系列方法获取响应数据。getResponseBody系列方法包括:
   
[java]  view plain copy
  1. public byte[] getResponseBody() throws IOException{...}  
  2. public byte[] getResponseBody(int maxlen) throws IOException{...}  
  3. Public InputStream getResponseBodyAsStream() throws IOException {...}  
  4. public String getResponseBodyAsString() throws IOException {...}  
  5. public String getResponseBodyAsString(int maxlen) throws IOException {...}  
 
    我比较喜欢getResponseBodyAsString方法,因为返回值类型是String,直接可以使用。不过, 提到String就必须想到charset   。响应数据的charset肯定由Web Server(Tomcat)设置的,HttpMethodBase是怎么知道的呢?

    我们看看getResponseBodyAsString()方法的代码:
   
[java]  view plain copy
  1. public String getResponseBodyAsString() throws IOException {  
  2.     byte[] rawdata = null;  
  3.     if (responseAvailable()) {  
  4.         rawdata = getResponseBody();  
  5.     }  
  6.     if (rawdata != null) {  
  7.         return EncodingUtil.getString(rawdata, getResponseCharSet());  
  8.     } else {  
  9.         return null;  
  10.     }  
  11. }  
 
    顾名思义,getResponseCharSet方法的功能就是获取响应数据的charset。那就看看她的代码吧:
   
[java]  view plain copy
  1. public String getResponseCharSet() {  
  2. return getContentCharSet(getResponseHeader("Content-Type"));  
  3. }  

    可见,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   中进行配置。
   
[xhtml]  view plain copy
  1. <Connector   
  2. URIEncoding="UTF-8"   
  3. useBodyEncodingForURI="true"    
  4. acceptCount="100"   
  5. connectionTimeout="20000"   
  6. disableUploadTimeout="true"   
  7. enableLookups="false"   
  8. maxHttpHeaderSize="8192"   
  9. maxSpareThreads="75"   
  10. maxThreads="150"   
  11. minSpareThreads="25"   
  12. port="8080"   
  13. redirectPort="8443"/>  

    URIEncoding属性就是URI的charset,上述配置表示 Tomcat认为URI的charset就是UTF-8。如果HttpClient也使用UTF-8作为QueryString的charset,那么 Tomcat就可以正确decoding。详情可以参考org.apache.tomcat.util.http.Parameters类的handleQueryParameters的方法:

   

[java]  view plain copy
  1. // -------------------- Processing --------------------  
  2. /** Process the query string into parameters 
  3.  */  
  4. public void handleQueryParameters() {  
  5.     // 省略部分代码  
  6.     processParameters( decodedQuery, queryStringEncoding );  
  7. }  

    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方法:

   

[java]  view plain copy
  1. /** 
  2.  * Parse request parameters. 
  3.  */  
  4. protected void parseParameters() {  
  5.     // 省略部分代码  
  6.     String enc = getCharacterEncoding();  
  7.     boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();  
  8.     if (enc != null) {  
  9.         parameters.setEncoding(enc);  
  10.         if (useBodyEncodingForURI) {  
  11.             parameters.setQueryStringEncoding(enc);  
  12.         }  
  13.     } else {  
  14.         parameters.setEncoding (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);  
  15.         if (useBodyEncodingForURI) {  
  16.             parameters.setQueryStringEncoding (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);  
  17.         }  
  18.     }  
  19.     parameters.handleQueryParameters();  
  20.     // 省略部分代码  
  21. }  


     默认的server.xml是没有配置useBodyEncodingForURI属性的,需要我们手动设置 。如果没有设置,Tomcat则认为其值为false。需要注意的是,如果URIEncoding和useBodyEncodingForURI同时设置,而且Body的charset已经设置,那么将以Body的charset为准 。

    设置POST请求Body的charset 

    设置Body charset的方法很简单,只要调用javax.servlet.ServletRequest接口的setCharacterEncoding方法即可,比如request.setCharacterEncoding("UTF-8")。需要注意的是,该方法必须在读取任何请求参数之前调用,才有效果。详情可以参见该方法的注释:
    

[java]  view plain copy
  1. /** 
  2.  * Overrides the name of the character encoding used in the body of this 
  3.  * request. This method must be called prior to reading request parameters 
  4.  * or reading input using getReader(). 
  5.  * 
  6.  * 
  7.  * @param env a <code>String</code> containing the name of 
  8.  * the character encoding. 
  9.  * @throws java.io.UnsupportedEncodingException if this is not a valid encoding 
  10.  */  
  11. public void setCharacterEncoding(String env) throws java.io.UnsupportedEncodingException;  


     也就是说,我们只有在调用getParameter或getReader方法之前,调用setsetCharacterEncoding方法,设置的charset才能奏效。

      响应数据的charset

    设置响应数据charset的方法很简单,只要调用javax.servlet.ServletResponse接口的setContentType或setCharacterEncoding方法即可,比如response.setContentType("text/html;charset=UTF-8")或response.setCharacterEncoding("UTF-8")。需要注意的是,这两个方法的调用时机也是有讲究的,详情可以参见他们的注释。

 

    如果Servlet正确设置了响应数据的charset,那么HTTP响应数据中就会包含Content-Type Header。HttpClient的getResponseBodyAsString方法就可以正确decoding响应数据。



总结 

    在开发Java Web应用的过程中,遇到中文乱码问题,应该是一件正常的事情。我们不必首先怀疑HtpClient或Tomcat有莫名奇妙的bug,往往都是我们使用不当或配置不当。凡事总有原因,总要相信科学嘛!如果想彻底了解中文编码的前因后果,我觉得可以从HTTP规范、Servlet规范、HttpClient的API文档和Tomcat的配置文档入手,必要时可以追踪HttpClient和Tomcat的代码。

这篇关于httpclient编码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1079760

相关文章

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

form表单提交编码的问题

浏览器在form提交后,会生成一个HTTP的头部信息"content-type",标准规定其形式为Content-type: application/x-www-form-urlencoded; charset=UTF-8        那么我们如果需要修改编码,不使用默认的,那么可以如下这样操作修改编码,来满足需求: hmtl代码:   <meta http-equiv="Conte

Apache HttpClient使用详解

转载地址:http://eksliang.iteye.com/blog/2191017 Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性。因此熟

4-4.Andorid Camera 之简化编码模板(获取摄像头 ID、选择最优预览尺寸)

一、Camera 简化思路 在 Camera 的开发中,其实我们通常只关注打开相机、图像预览和关闭相机,其他的步骤我们不应该花费太多的精力 为此,应该提供一个工具类,它有处理相机的一些基本工具方法,包括获取摄像头 ID、选择最优预览尺寸以及打印相机参数信息 二、Camera 工具类 CameraIdResult.java public class CameraIdResult {

Python字符编码及应用

字符集概念 字符集就是一套文字符号及其编码的描述。从第一个计算机字符集ASCII开始,为了处理不同的文字,发明过几百种字符集,例如ASCII、USC、GBK、BIG5等,这些不同的字符集从收录到编码都各不相同。在编程中出现比较严重的问题是字符乱码。 几个概念 位:计算机的最小单位二进制中的一位,用二进制的0,1表示。 字节:八位组成一个字节。(位与字节有对应关系) 字符:我们肉眼可见的文字与符号。

项目一(一) HttpClient中的POST请求和GET请求

HttpClient中的POST请求和GET请求 一、HttpClient简述 HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLU

在Eclipse环境下修改Tomcat编码的问题

问题: 由于BMS需要设置UTF-8编码,要不就会出现中文乱码问题; 一、项目保持UTF-8格式; 二、由于可能会多次移除项目、加载项目,不想每次都要修改tmp0\conf 原因: 如果在eclipse中配置了tomcat后,其实,tomcat所用的所有tomcat配置文件,都不是catalina_home/config下面的xml文件,而是在eclipse所创建的Serve

在Unity环境中使用UTF-8编码

为什么要讨论这个问题         为了避免乱码和更好的跨平台         我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本,默认是看不到在VS中的编码格式,下面我介绍一种简单快

霍夫曼编码/译码器

赫夫曼树的应用 1、哈夫曼编码   在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用