wechat4j获取用户昵称乱码修复

本文介绍如何解决使用wechat4j获取微信用户昵称时出现的中文乱码问题,通过对wechat4j源码进行调整,确保正确处理UTF-8编码。

项目对接微信公众号平台时,微信的官方给出的建议是使用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源码的目录:

  1. apache.http就是httpcore-4.3.3.jar的源码。
  2. sword是wechat4j.jar的源码。
  3. tomcat 7 是把该项目引入到一个web项目后,自动出现的,我顺带把wechat4j所需要的jar包添加到lib包下。
  4. 本来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;
}

看完本篇文章,大家是否略有所获?

##What is wechat4j? wechat develop framework for java(微信开发框架JAVA版,最简单易用微信开发框架) ##wechat4j可以用来干什么? wechat4j是一个帮助你开发微信应用的jar包,使用它,你开发微信公众号应用只需要几秒钟的时间,完全不用关注太细节的东西。 ##wechat4j快速开始 可以去下载wechat4j示例项目[wechat4jDemo](https://github.com/repoproject/wechat4jDemo),然后在其基础之上修改即可。如果你要自己搭建,那么使用wechat4j只需要三步就可以搭建微信开发环境。 1. 创建一个web工程,导入jdk和相关的web工程jar包。 2. 下载wechat4j.jar包,下载地址[wechat4j下载](https://github.com/sword-org/wechat4j/releases)。 3. 创建wechat4j配置文件,在src目录下(java根目录)创建wechat4j.properties文件,配置你微信公众号的相关信息。内容如下: ```properties #you wechat token wechat.token=token #wechat appid wechat.appid=appid #wechat app secret wechat.appsecret=secret ``` 你也可以在jar包的META-INF目录下找到wechat4j.properties.sample文件,复制到src目录下修改名称即可。wechat4j.properties配置文件的详细配置项意义参见[wechat4j配置文件解读](https://github.com/sword-org/wechat4j/wiki/wechat4j%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%A7%A3%E8%AF%BB) 通过以上步骤,你的微信工程就完全搭好了。 ##wechat4j 运行环境 wechat4j要求的最低java运行环境是jdk1.6 wechat4j.jar的依赖jar包 > * commons-codec.jar 1.3以上 > * commons-lang3.jar > * log4j.jar 1.2以上 > * fastjson-1.2.0.jar > * sword-lang-1.2 (https://github.com/sword-org/sword-lang/releases) > * fluent-hc-4.3.6.jar(httpclient依赖) > * httpclient-4.3.6.jar > * httpcore-4.3.3.jar (httpclient依赖) > * servlet-api.jar 如果你是web工程,导入支持web工程的包就会包括,例如tomcat包 你可以去集中下载这些jar包的集合[wechat4j所需jar下载](http://files.cnblogs.com/chengn/wechat4j-lib.rar),也可以去maven库或者对应jar包的项目官网下载. ##开发自己的微信应用 wechat4j开发环境搭好之后,就可以开始开发自己的微信应用了。比如我有一个微信号的token是lejian,下面就以她为例子来说明。 ###创建自己公众号服务类 创建自己的微信公众号服务类,需要继承wechat4j的WechatSupport类,然后实现其抽象方法即可,下面以文本消息处理为例子 ```java public class Lejian extends WechatSupport{ public Lejian(HttpServletRequest request) { super(request); } @Override protected void onText() { this.wechatRequest.getFromUserName(); String content = "test ok"; responseText(content); } } ``` 上面代码中的``onText()``是WechatSupport的抽象方法,需要你自己的类来实现,表示对文本消息的处理,示例中是接收到用户的消息之后,返回给用户“test ok”文本消息。 ###创建微信服务地址 创建微信服务地址(微信公众平台中配置的自己服务器地址)servlet类。如果是springmvc则创建对应的controller,如果是struts则创建对应的action类。servlet类示例如下: ```java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Lejian lejian = new Lejian(request); String result = lejian.execute(); response.getOutputStream().write(result.getBytes()); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Lejian lejian = new Lejian(request); String result = lejian.execute(); response.getOutputStream().write(result.getBytes()); } ``` 通过上面两步你的微信服务就可以运行了 ##如何得到微信的请求参数 继承了``WechatSupport``类之后,你可以通过``wechatRequest.getFromUserName()``类似的方法来得到微信服务器请求的参数。详细信息请阅读[微信请求参数](https://github.com/sword-org/wechat4j/wiki/%E5%BE%97%E5%88%B0%E5%BE%AE%E4%BF%A1%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) ##如何设置响应参数 继承了``WechatSupport``类之后,你可以通过``wechatResponse.setFromUserName(fromUser)``类似的方法来设置给微信服务器的响应参数。详细信息请阅读[响应微信服务器参数](https://github.com/sword-org/wechat4j/wiki/%E8%AE%BE%E7%BD%AE%E5%93%8D%E5%BA%94%E5%BE%AE%E4%BF%A1%E5%8F%82%E6%95%B0) ##如何响应用户信息 以文本信息为例,响应文本信息只需要在你的``onText``方法中使用``responseText(content)``即可(参见上面的代码例子) ##wechat4j示例项目 * [wechat4jDemo](https://github.com/repoproject/wechat4jDemo) 如果你有好的demo项目,请邮件或者修改本文件然后pull request给我,我会列在上面。 ##技术支持 * [wechat4j开发者文档中心](http://www.chengn.com/wechat4j/) * [wechat4j开发文档](https://github.com/sword-org/wechat4j/wiki) * wechat4j技术交流QQ群 **423134346** * 支持邮件 sword_org@163.com * wechat4j暂无论坛,欢迎开通论坛交流版块,如果开通请邮件,我会添加到这里。 ##贡献代码 1. 如果你觉得本项目不错,希望你能够点击一下右上角的star 2. 如果你希望参与改进本项目,那么请点击右上角的fork,修改之后pull request即可。如果你的贡献不错,你就会收到加入[sword](https://github.com/sword-org)开源社区的邀请。 3. 如果你发现了一个bug,请你创建一个issue来报告。 非常非常欢迎你能够参与本项目的建设,每人做出一点点贡献,对整个项目来说就是一个非常大的贡献,希望集合众人的力量,让项目走的更好,能够为更多的人服务。 ###贡献者列表 * [@chengn](https://github.com/chengn) * [@truecn](https://github.com/truecn) * [@Zhangys-hh](https://github.com/Zhangys-hh)
<think> 我们参考了提供的引用内容,尤其是引用[2]和引用[4],它们直接提到了在uniapp中获取用户头像和昵称的相关调整和方法。 根据引用[2]:从2022年10月25日24时后,微信小程序调整了用户头像昵称获取规则,使用`wx.getUserProfile`获取用户头像将统一返回默认灰色头像,昵称将统一返回“微信用户”。因此,传统的获取方式已经不再适用。 引用[4]提供了一种新的方法:使用微信小程序提供的“头像和昵称填写能力”。具体来说,在页面中使用`<input type="nickname">`组件,用户点击输入框时,会弹出微信自带的昵称填写面板。用户填写后,通过绑定事件(如`@blur`)来获取用户输入的昵称。 另外,引用[3]提到需要调用接口获取登录凭证(code),进而换取用户登录态信息(openid, session_key等),但获取用户昵称和头像的新方法并不依赖于此,而是通过新的组件方式。 因此,在uniapp开发的微信小程序中获取用户昵称的推荐方法如下: 1. 在页面的布局文件中,使用`input`组件,并设置`type`为`nickname`。这样,当用户点击输入框时,会自动弹出微信的昵称填写面板。 2. 通过绑定事件(如`blur`事件)来获取用户填写的昵称。 示例代码如下: ```html <template> <view> <!-- 昵称输入框 --> <input type="nickname" placeholder="请输入昵称" @blur="onNicknameBlur" /> <!-- 显示昵称 --> <view>昵称:{{nickname}}</view> </view> </template> <script> export default { data() { return { nickname: '' } }, methods: { onNicknameBlur(e) { // 事件对象e中存储了用户输入的昵称 this.nickname = e.detail.value; console.log('用户昵称:', this.nickname); } } } </script> ``` 注意:这种方法获取的是用户手动输入的昵称,而不是自动从微信资料中获取的(因为微信已经不允许直接获取)。同样地,获取头像可以使用`<button open-type="chooseAvatar">`组件,并绑定`@chooseavatar`事件来获取用户选择的头像。 头像获取示例: ```html <button open-type="chooseAvatar" @chooseavatar="onChooseAvatar"> <image v-if="avatarUrl" :src="avatarUrl" mode="aspectFill"></image> <text v-else>选择头像</text> </button> ``` 在脚本中: ```javascript methods: { onChooseAvatar(e) { this.avatarUrl = e.detail.avatarUrl; } } ``` 总结:由于微信的规则调整,现在获取用户昵称和头像需要使用微信提供的组件(`input[type=nickname]`和`button[open-type=chooseAvatar]`),用户主动操作后,才能获取用户设置的昵称和头像。 [^1]: 微信小程序开发文档 - 获取接口调用凭据 [^2]: 微信小程序用户头像昵称获取规则调整公告 [^3]: 微信小程序登录流程 [^4]: uniapp开发微信小程序使用头像和昵称填写能力 </think> 在 Uniapp 开发微信小程序时,获取用户昵称等信息的方法需遵循微信官方的最新规范(2022年10月调整)。以下是完整实现方案: ### ✅ 正确获取用户昵称的方案 根据微信规则 [^2],禁止直接通过 API 获取昵称,需使用**官方组件**: ```html <!-- 昵称输入组件 --> <input type="nickname" class="nickname-input" placeholder="请输入昵称" @blur="onNicknameBlur" /> ``` ```javascript methods: { onNicknameBlur(e) { const nickname = e.detail.value; console.log("用户昵称", nickname); this.nickname = nickname; // 存储到 data } } ``` ### ✅ 获取用户手机号方案 需先通过登录流程获取 `code` [^3]: ```javascript uni.login({ provider: 'weixin', success: (res) => { const code = res.code; // 登录凭证 this.getPhoneNumber(code); // 传给后端换手机号 } }); ``` ```html <!-- 手机号授权按钮 --> <button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber"></button> ``` ### ✅ 获取用户权限流程 1. **初始化登录** [^3] ```javascript uni.checkSession({ success: () => { /* session 有效 */ }, fail: () => { uni.login() } // 重新登录 }); ``` 2. **获取用户信息** ```javascript uni.getUserProfile({ desc: '用于完善会员资料', success: (res) => { // res.userInfo 包含基础信息 } }); ``` ### ️ 关键注意事项 1. 头像获取返回灰色默认图 [^2],需引导用户主动上传: ```html <button open-type="chooseAvatar" @chooseavatar="onChooseAvatar"> 上传头像 </button> ``` 2. 需在 `manifest.json` 配置权限: ```json "mp-weixin": { "permission": { "scope.userInfo": {"desc": "获取昵称"}, "scope.phoneNumber": {"desc": "获取手机号"} } } ``` ### 完整流程示意图 ```mermaid graph LR A[用户点击授权] --> B[uni.login 获取 code] B --> C[后端用 code 换取 openid/session_key] C --> D[前端通过组件获取昵称/手机号] D --> E[数据加密后传后端解密] ``` > 重要提示:所有敏感信息(如手机号)必须通过**后端解密** [^3],前端仅传递加密数据。 [^1]: 微信小程序接口调用凭证说明 [^2]: 微信用户头像昵称获取规则调整公告 [^3]: 微信登录凭证及数据解密流程 [^4]: Uniapp 头像填写能力实现 --- ### 相关问题 1. 如何解决微信小程序返回默认灰色头像的问题? 2. 用户拒绝授权手机号后应该如何处理? 3. Uniapp 中如何实现微信登录态的持久化管理? 4. 为什么获取用户手机号需要后端配合解密? 5. 微信小程序的用户信息授权流程与其他平台有何不同?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉默王二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值