最近在搞微信的开发者模式,对于第一次接触这东西的人来说还真是没有头绪,因为需要跟微信进行交互,难免会碰到一些问题,不像我们在本机开发那样,很快就能把逻辑代码写完。今天主要是让测试耽误了时间,由于微信只能绑定域名,不能使用ip地址,但是在公司中域名直接解析到线上服务器,但是线上服务器该域名下已经有服务在运行,所以不能使用线上服务器直接进行开发测试,也不能把已有的服务号打开开发者模式,因为打开开发者模式之后好多已有的自动回复就不能使用了,总之遇到了各种问题,记录下开发过程。
申请测试账号
进入申请地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
点击登录按钮,会弹出一个二维码,使用手机微信扫描即可
登录成功之后会出现下面的界面
appID和appsecret在调用微信接口的时候会用到,这里是微信自动给我们生成好的,直接使用就可以。
URL:是我们自己服务器的URL,用户微信推送用户消息和事件用的。这里只能配置域名,不能使用IP。这个URL是用来接收微信的token认证和消息事件用的,我们自己的服务器要能对这个url的请求进行处理并相应,所以要求该域名必须是外网可以访问的,否则在点“提交”按钮的时候会报token异常。
Token:是验证签名用的,此处填写的要与代码中的Token保持一致否则验证不过。
URL和Token都填好之后点击提交按钮,若验证通过,会返回配置成功,否则返回配置失败。
配置失败的原因可能有几个:
1、URL地址不能访问
2、Token填写错误
注意:在点击提交按钮之前一定要先把服务端的代码写好,否则会返回配置失败,具体实现代码在后面演示。
到此为止,我们的接口配置信息就算完成了。
扫描测试号二维码就可以来测试我们的功能了
Token认证
我配置的URL是,http://www.xxx.com/XX/wxProcess/service,所以微信会把所有的请求都发到我的/service路径上,包括Token认证和事件处理,我使用两个方法来处理的,但是请求类型不同,Token认证是用的GET请求,事件处理使用的是POST请求。演示代码如下:
/**
* 打开开发者模式签名认证
* @param signature
* @param timestamp
* @param nonce
* @param echostr
* @return
*/
@ResponseBody
@RequestMapping(value = "/service", method = RequestMethod.GET)
public Object defaultView(String signature, String timestamp, String nonce, String echostr) {
if (echostr == null || echostr.isEmpty()) {
return nonce;
}
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
return echostr;
}
return nonce;
}
/**
* 事件处理
* @param signature
* @param timestamp
* @param nonce
* @param message
* @return
*/
@ResponseBody
@RequestMapping(value = "/service", method = RequestMethod.POST, consumes = "text/xml", produces = "text/xml;charset=UTF-8")
public String defaultViewHandler(String signature, String timestamp, String nonce, @RequestBody String message) {
String result = "";
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
result = coreService.processRequest(message);
}
return result;
}
做签名认证的时候会调用我的defaultView方法,其他的事件都走defaultViewHandler方法。
coreService.processRequest(): 是对所有的请求事件进行处理
CoreService.java主要代码实现如下:
@Override
public String processRequest(String msg) {
String respMessage = null;
try {
// 默认返回的文本消息内容
String respContent = "";
// xml请求解析
Map<String, String> requestMap = MessageUtil.parseXml(msg);
// 发送方帐号(open_id)
String fromUserName = requestMap.get("FromUserName");
// 公众帐号
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
String eventType = requestMap.get("Event");
// 存在事件类型且类型有效 或者 消息类型可以处理,才需要进行身份处理,防止没有意义的事件引起发送大量绑定消息
if ((!StringUtils.isEmpty(eventType) && !eventArrays.contains(eventType)) || !msgArrays.contains(msgType)) {
return "";
}
respMessage = handleWxMessage(requestMap);
} catch (Exception e) {
LOGGER.error("error", e);
e.printStackTrace();
}
return respMessage;
}
@Override
public String handleWxMessage(Map<String, String> requestMap) {
// 发送方帐号(open_id)
String fromUserName = requestMap.get("FromUserName");
// 公众帐号
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
// 回复文本消息
String respContent = "";
BaseMessage textMessage = new TextMessage();
textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
switch (msgType) {
case MessageUtil.REQ_MESSAGE_TYPE_TEXT:
// 文本类型消息,暂时设置成用户发什么就回复什么
String content = requestMap.get("Content");
respContent = "";
break;
case MessageUtil.REQ_MESSAGE_TYPE_VOICE:
// 使用语音识别功能需要开启微信公众号相关权限
// 语音识别结果
String recognizeText = requestMap.get("Recognition");
respContent = "";
break;
case MessageUtil.REQ_MESSAGE_TYPE_EVENT:
// 事件类型
String eventType = requestMap.get("Event");
// 事件KEY值,qrscene_为前缀,后面为二维码的参数值
String eventKey = requestMap.get("EventKey");
// 自定义事件
switch (eventType) {
// 用户未关注时,进行关注后的事件推送
case MessageUtil.EVENT_TYPE_SUBSCRIBE:
if (isUserBuyLastActivity(fromUserName)) {
textMessage = getAddClassTextMessage(fromUserName);
} else {
textMessage = getDefaultTextMessage(fromUserName);
}
Integer eventKeyValue = 1;// 带参数的情景值
if (!StringUtils.isEmpty(eventKey) && eventKey.contains("qrscene_")) {
eventKey = eventKey.replace("qrscene_", "");
// 渠道ID
eventKeyValue = Integer.parseInt(eventKey);
}
subscribeAction(fromUserName, eventKeyValue);
break;
// 取消关注
case MessageUtil.EVENT_TYPE_UNSUBSCRIBE:
unSubscribeAction(fromUserName);
break;
// 用户已关注时的事件推送
case MessageUtil.EVENT_TYPE_SCAN:
textMessage = getDefaultTextMessage(fromUserName);
break;
default:
respContent = "";
break;
}
break;
default:
respContent = "";
break;
}
if (textMessage instanceof TextMessage) {
// 文本内容为空且是文本消息返回空字符串,防止微信发送异常消息
String content = ((TextMessage) textMessage).getContent();
if (StringUtils.isEmpty(respContent) && StringUtils.isEmpty(content)) {
return "";
}
if (org.apache.commons.lang.StringUtils.isBlank(content)) {
((TextMessage) textMessage).setContent(respContent);
}
}
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(new Date().getTime());
String xmlString = MessageUtil.messageToXml(textMessage);
return xmlString;
}
上面只是展示了部分核心代码,具体代码可以去我的GitHub下载,地址:https://github.com/liuyanmin/wxDemo
下面是微信开发相关的链接:
申请个人测试账号:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
微信被动回复消息开发文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543
微信错误码文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543