httpclient中文乱码问题

问题描述:

在使用httpclient开发接口时,客户端获取服务端返回的数据中包含中文部分出现???不能正常显示。

这里说下我使用的中间件的版本:

httpclient4.3.3


问题分析:

当时的思路是看乱码出在服务端还是客户端,因为是自己测试接口 代码里面服务端就打印输出了下发现是正常的,所以开始的问题寻找的出发点就放在了客户端获取数据乱码

的点上。下面贴出客户端测试的代码

String url = "http://localhost:8080/****";
StringBuffer sb = new StringBuffer();

sb.append(****);

String reqXml=sb.toString();
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(url);
HttpEntity resEntity;
HttpEntity reqEntity = null;

//这里为了编码格式统一 专门赋值为了utf-8类型
reqEntity = new StringEntity(reqXml,Consts.UTF_8);
//httppost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); 这个是在网上找的解决办法 发现放上去仍然不行
httppost.setEntity(reqEntity);
CloseableHttpResponse response;
response = httpclient.execute(httppost);
resEntity = response.getEntity();
String res = EntityUtils.toString(resEntity,"UTF-8");
System.out.println(res);

上面的方法是网上介绍比较多的解决httpclient乱码的办法,我试了但是依然是乱码???

其实在检查的过程中我发现EntityUtils.toString(resEntity,"UTF-8");这个设置是没有起到作用的,这里摘录下部分源码

 public static String toString(
            final HttpEntity entity, final Charset defaultCharset) throws IOException, ParseException {
        Args.notNull(entity, "Entity");
        final InputStream instream = entity.getContent();
        if (instream == null) {
            return null;
        }
        try {
            Args.check(entity.getContentLength() <= Integer.MAX_VALUE,
                    "HTTP entity too large to be buffered in memory");
            int i = (int)entity.getContentLength();
            if (i < 0) {
                i = 4096;
            }
            Charset charset = null;
            try {
                final ContentType contentType = ContentType.get(entity);
                if (contentType != null) {
                    charset = contentType.getCharset();
                }
            } catch (final UnsupportedCharsetException ex) {
                throw new UnsupportedEncodingException(ex.getMessage());
            }
            if (charset == null) {
                charset = defaultCharset;
            }
            if (charset == null) {
                charset = HTTP.DEF_CONTENT_CHARSET;
            }

也就是说当ContentType.get(entity);获取的编码如果不为空的话 你设置的编码也就不起作用了。所以我这里没起作用,我就直接修改了这段代码拿出来进行测试,发现改为utf-8还是不起作用。折腾了几天 实在想不出什么原因,后来就换了思路猜想是不是在服务端返回数据后 写入response的时候乱码的了。这里声明下我这边框架用的springmvc

用的注解@ResponseBody


到此问题的点也就清晰了,这个地方我的方法返回的是String类型,由于springmvc3.2之后@RequestMapping使用了RequestMappingHandlerAdapter来处理请求,对于@ResponseBody,当为string时,会调用默认构造方法里面add的StringHttpMessageConverter,需要注意的是,这个converter默认的编码是“ISO-8859-1” 这是导致中文乱码的原因。这里介绍几种解决方法(我再这里采用的是方法二)

方法一(转自http://josh-persistence.iteye.com/blog/2085015

)查看StringHttpMessageConverter的源码,发下StringHttpMessageConverter类中的默认编码是ISO-8859-1编码,改编码是西欧字符集编码,显然不会支持中文。

 

Java代码   收藏代码
  1. public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");  
  2.   
  3. private final Charset defaultCharset;  
  4.   
  5. private final List<Charset> availableCharsets;  

 

 

而且从上面的代码中可以看出,与字符相关的3个变量都是final的,意味着我们不能通过set或者构造器的注入去动态的更改上面3个值。

 

进一步分析源码可以看出,StringHttpMessageConverter继承与AbstractHttpMessageConverter<String>,分析该抽象类得,相关的操作字符编码的方法可以重写,于是我们可以自定义一个类继承StringHttpMessageConverter,然后重写相关的方法,代码如下:

 

Java代码   收藏代码
  1. /** 
  2.  *  
  3.  */  
  4. package com.chuanliu.platform.activity.basic.converter;  
  5.   
  6.   
  7.   
  8. import java.io.IOException;  
  9. import java.nio.charset.Charset;  
  10. import java.util.Arrays;  
  11. import java.util.List;  
  12.   
  13. import org.springframework.http.HttpOutputMessage;  
  14. import org.springframework.http.MediaType;  
  15. import org.springframework.http.converter.StringHttpMessageConverter;  
  16. import org.springframework.util.StreamUtils;  
  17.   
  18. /** 
  19.  * 用于处理中文乱码问题: Spring bug - 
  20.  *  
  21.  * In StringHttpMessageConverter, the default char set is ISO-8859-1(西欧字符集) 
  22.  *  
  23.  * @author Josh Wang(Sheng) 
  24.  * 
  25.  * @email  josh_wang23@hotmail.com 
  26.  */  
  27. public class UTF8StringHttpMessageConverter extends StringHttpMessageConverter {  
  28.   
  29.         private static final MediaType UTF8 = new MediaType("text""plain",   
  30.            Charset.forName("UTF-8"));  
  31.   
  32.     private boolean writeAcceptCharset = true;  
  33.       
  34.     @Override  
  35.     protected void writeInternal(String s, HttpOutputMessage outputMessage)  
  36.             throws IOException {  
  37.         if (this.writeAcceptCharset)  
  38.             outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());  
  39.           
  40.         Charset charset = UTF8.getCharSet();  
  41.           
  42.         StreamUtils.copy(s, charset, outputMessage.getBody());  
  43.           
  44.     }  
  45.   
  46.     @Override  
  47.     protected List<Charset> getAcceptedCharsets() {  
  48.         return Arrays.asList(UTF8.getCharSet());  
  49.     }  
  50.   
  51.     @Override  
  52.     protected MediaType getDefaultContentType(String t) throws IOException {  
  53.         return UTF8;  
  54.     }  
  55.   
  56.     public boolean isWriteAcceptCharset() {  
  57.         return writeAcceptCharset;  
  58.     }  
  59.   
  60.     public void setWriteAcceptCharset(boolean writeAcceptCharset) {  
  61.         this.writeAcceptCharset = writeAcceptCharset;  
  62.     }  
  63.       
  64.       
  65. }  

 

 

定义好上面的类后,只需要将该类注册到Spring的annotaion 处理序列中即可,于是当@ResponseBody中返回的类型是String类型时,Spring将会调用上面自定义类中复写的方法,从而返回UTF-8的编码:

 

 

Xml代码   收藏代码
  1. <mvc:annotation-driven>  
  2.     <mvc:message-converters register-defaults="true">  
  3.     <bean class="com.chuanliu.platform.activity.basic.converter.UTF8StringHttpMessageConverter"/>  
  4.   </mvc:message-converters>  
  5. </mvc:annotation-driven>  

解决方法2:最简单的方法:

在@Responsebody标注的方法上加上:produces="application/json;charset=utf-8"

如:

Java代码   收藏代码
  1. @RequestMapping(value="/circle/{cid}", produces="application/json;charset=utf-8")  
  2. @ResponseBody  
produces:    指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;


@RequestMapping(value = "/produces", produces = "application/json"):表示将功能处理方法将生产json格式的数据,此时根据请求头中的Accept进行匹配,如请求头“Accept:application/json”时即可匹配;

@RequestMapping(value = "/produces", produces = "application/xml"):表示将功能处理方法将生产xml格式的数据,此时根据请求头中的Accept进行匹配,如请求头“Accept:application/xml”时即可匹配。

   

此种方式相对使用@RequestMapping的“headers = "Accept=application/json"”更能表明你的目的


解决方法3:指定返回值为对象不要返回String

既然上面分析了其原因是上面的StringHttpMessageConverter类中使用了ISO-8859-1编码,那么如果业务逻辑允许,我们完全可以不要返回一个字符串(String),完全可以返回一个对象,这样Spring默认会去调用一个叫MappingJackson2HttpMessageConverter的类,该类不仅会将你返回的对象转换成JSON返回,而且该类中使用的是我们想要的UTF-8字符集,




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值