
项目对接微信公众号平台时,微信的官方给出的建议是使用wechat4j。官方建议的,自然心里踏实,但实际用起来时发现wechat4j埋有很多雷,最让人心烦意乱的就是中文乱码问题。
之前写过一篇为JAXB和response设置编码,解决wechat4j中文乱码,解决的是智能输入时反馈信息的乱码,遇到同样问题的伙伴可以去参考一下解决方案。
今天要写的是利用wechat4j获取用户昵称乱码的问题。
一、问题描述
UserManager userManager = new UserManager();
org.sword.wechat4j.user.User user = userManager.getUserInfo(tokenResponse.getOpenid());
thirdLoginMember.setThird_name(user.getNickName());
项目运行时,一旦昵称为中文,此方法获得的昵称就是乱码,引发乱码的原因其实很直白,无非就是编码方式不匹配。
二、问题分析
我们来追本溯源,看看以下的代码,我们能感受到问题具体出在哪。
wechat4j中的代码
public User getUserInfo(String openId) {
return getUserInfo(openId, null);
}
public User getUserInfo(String openId, LanguageType lang) {
String url = USER_INFO_GET_URL + TokenProxy.accessToken() + "&openid=" + openId;
if (lang != null) {
url += "&lang=" + lang.name();
}
String resultStr = HttpUtils.get(url);
logger.info("return data " + resultStr);
try {
WeChatUtil.isSuccess(resultStr);
} catch (WeChatException e) {
logger.error(e.getMessage());
e.printStackTrace();
return null;
}
User user = JSONObject.parseObject(resultStr, User.class);
return user;
}
public static String get(String url){
return httpGet(url);
}
private static String httpGet(String url) {
try {
HttpEntity entity = Request.Get(url).
execute().returnResponse().getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} catch (Exception e) {
logger.error("get请求异常," + e.getMessage() + "\n get url:" + url);
e.printStackTrace();
}
return null;
}
httpcore-4.3.3.jar中的代码
public static String toString(final HttpEntity entity)
throws IOException, ParseException {
return toString(entity, (Charset)null);
}
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) {
if (defaultCharset == null) {
throw new UnsupportedEncodingException(ex.getMessage());
}
}
if (charset == null) {
charset = HTTP.UTF8_CONTENT_CHARSET;
}
logger.info("输出流的转码方式为:" + charset);
final Reader reader = new InputStreamReader(instream, charset);
final CharArrayBuffer buffer = new CharArrayBuffer(i);
final char[] tmp = new char[1024];
int l;
while((l = reader.read(tmp)) != -1) {
buffer.append(tmp, 0, l);
}
return buffer.toString();
} finally {
instream.close();
}
}
从上述代码中可得知,wechat4j在取得到参数后,调用httpcore-4.3.3.jar中的EntityUtils类的toString()方法,该方法默认使用的编码方式是“ISO-8859-1”。看到这里,你也许就恍然大悟了,原来如此嘛。那,该如何改动呢?
三、问题解决之道
wechat4j是一个jar包,git上提供了源码下载,你可以选择直接在项目中引入wechat4j.jar,也可以选择把源码作为一个导入项目引入到项目中,我选择的是第二种做法,这种方案方便我们调试,以及修改源码。
在说解决之道之前,我先啰嗦一点关于eclipse中项目之间引用的注意事项。
上图是我项目中wechat4j源码的目录:
- apache.http就是httpcore-4.3.3.jar的源码。
- sword是wechat4j.jar的源码。
- tomcat 7 是把该项目引入到一个web项目后,自动出现的,我顺带把wechat4j所需要的jar包添加到lib包下。
- 本来wechat4j是一个Java项目。
关于deployment assembly我并没有很好的理解其作用(Define packaging structure for this Java EE Web Application project.),大致的意思是把被引入的项目编译成jar包供引用项目使用。
build path中引入项目。
project reference。
关于这三个之间的关联关系,我真是没有搞明白,敬请小伙伴们指导。
PS:请特别注入,针对wechat4j的引入,被引入项目(ymeng)中不能再放httpcore-4.3.3.jar,否则wechat4j中的httpcore将不会起作用。
那么接下来,我们该好好看看怎么解决了,其实方法依然可知。
第一种,EntityUtils.toString()方法中设置编码方式。
return entity != null ? EntityUtils.toString(entity,"utf-8") : null;
这个其实应该交由wechat4j来做的, 但它坑了。
第二种,EntityUtils.toString方法中设置默认的编码方式为utf-8.
if (charset == null) {
charset = HTTP.UTF8_CONTENT_CHARSET;
}
看完本篇文章,大家是否略有所获?