问题描述:
在使用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编码,改编码是西欧字符集编码,显然不会支持中文。
而且从上面的代码中可以看出,与字符相关的3个变量都是final的,意味着我们不能通过set或者构造器的注入去动态的更改上面3个值。
进一步分析源码可以看出,StringHttpMessageConverter继承与AbstractHttpMessageConverter
定义好上面的类后,只需要将该类注册到Spring的annotaion 处理序列中即可,于是当@ResponseBody中返回的类型是String类型时,Spring将会调用上面自定义类中复写的方法,从而返回UTF-8的编码:
解决方法2:最简单的方法:
在@Responsebody标注的方法上加上:produces="application/json;charset=utf-8"
如:
@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字符集,