微信公众号开发-消息的接收与回复(文本)
这一期我们主要讲一下,服务号的文本消息接收与被动回复。
首先我们看一下微信给出的api
官方文档给出的api中有那么几个重要信息,第一个微信是将手机发送给微信服务号的消息,以Xml 的形式又以Post 的请求方式下发给我的自己填写的URL上。
画个图好理解一下
那么有了这几个内容,我们就很容易获取文本消息了。
话不多说,直接上代码讲思路
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
/**
* @fileName:EventController
* @author:18013
* @createTime:2019-05-24:15:23
*/
@RestController
@RequestMapping("/weixin")
public class EventController {
@Autowired
EventService eventService;
/**
* 微信服务器下发的消息
* @param request
* @return
*/
@PostMapping("/validate")
public String weixinConnect(HttpServletRequest request) throws IllegalAccessException, InstantiationException {
//用来接收微信下发的xml信息
BufferedReader bufferedReader = null;
StringBuffer stringBuffer =null;
String str;
try {
bufferedReader = request.getReader();
stringBuffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
stringBuffer.append(str);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(bufferedReader!=null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//userInfo 用户信息(xml)
String userInfo = stringBuffer.toString();
if(userInfo.contains("Content")){
// 解析xml,根据文本内容,业务逻辑,回复相应的消息给用户
final String replyContentMsg = eventService.replyContentMsg(userInfo);
System.out.println(replyContentMsg);
return replyContentMsg;
}
return "";
}
}
在回复消息前,我们需要解析xml,获取里面文本内容,openid等。
这边由于微信发送的xml ,格式十分相似,所以我建了一个基础消息model,其他用以继承便可。
import lombok.Data;
/**
* @fileName:BaseWxModel
* @author:ccl
* @createTime:2019-05-29:13:39
*/
@Data
public class BaseWxModel {
private String toUserName; //开发者微信号
private String fromUserName;// 发送方帐号(一个OpenID)
private String createTime;// 消息创建时间 (整型)
private String msgType;// 消息类型,event
private String msgId; //用于排查
}
下面贴这期关键 文本消息的bean
import lombok.Data;
/**
* @fileName:ContentMsg
* @author:ccl
* @createTime:2019-05-29:13:49
*/
@Data
public class ContentMsg extends BaseWxModel{
private String content;//文本
}
有了这个模型 我们可以进行xml的解析了,这里我用的是反射思想。
/**
* 解析收到的消息
* @param message
* @return
*/
public ContentMsg receiveContentMsg(String message){
ContentMsg contentMsg = new ContentMsg();
// 遍历类
for (Class clazz = contentMsg.getClass();!clazz.equals(Object.class);clazz=clazz.getSuperclass()){
Field fields[] = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String column = field.getName();
//获取属性值
String colunmValue = XmlUtils.getColunmValue(message, column.toUpperCase().substring(0, 1) + column.substring(1));
if (EmptyUtils.isNotEmpty(colunmValue)) {
try {
Method method = clazz.getDeclaredMethod("set" + column.toUpperCase().substring(0, 1) + column.substring(1), String.class);
method.invoke(contentMsg, colunmValue);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
log.info("收到消息:{}",contentMsg.getContent());
return contentMsg;
}
下面再贴两个我写的工具类
大家先看我的getColunmValue 方法 ,这个xml解析是我根据微信回复内容格式基本一致。取巧写出来的。很简单方便,
因为标签包裹的只有两种形式, < column ><![CDATA[columnValue]]></ column> 和 < column>< columnValue ></ column> 这两种。
parseToXml 是根据反射思想写的,大家要是不懂的话,可以留言问我 或者直接联系我。
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @fileName:XmlUtils
* @author:ccl
* @createTime:2019-05-24:15:33
*/
public class XmlUtils {
/**
* 获取xml中相应的字段名
* @param str
* @return
*/
public static String getColunmValue(String str, String column) {
String beginStr = "<" + column + "><![CDATA[";
String endStr = "]]></" + column + ">";
int i = str.indexOf(beginStr);
int i2 = str.indexOf(endStr);
if (i < 0) {
beginStr = "<" + column + ">";
endStr = "</" + column + ">";
i = str.indexOf(beginStr);
i2 = str.indexOf(endStr);
if (i < 0) {
return null;
}
}
return str.substring(i + beginStr.length(), i2);
}
public static String parseToXml(Object o) throws IllegalAccessException, InstantiationException {
StringBuffer stringBuffer = new StringBuffer();
Method method;
stringBuffer.append("<xml>");
for (Class clazz = o.getClass();!clazz.equals(Object.class);clazz=clazz.getSuperclass()){
Field fields[] = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String column = field.getName();
if(EmptyUtils.isNotEmpty(field.get(o))){
try {
method= clazz.getDeclaredMethod("get" + column.toUpperCase().substring(0, 1) + column.substring(1));
if (!method.getName().equals("getMsgId")){
String result = (String) method.invoke(o);
stringBuffer.append("<"+method.getName().substring(3)+">");
if (!method.getName().equals("getCreateTime")){
stringBuffer.append("<![CDATA["+result+"]]></"+ method.getName().substring(3)+">");
}else {
stringBuffer.append(result+"</"+ method.getName().substring(3)+">");
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
stringBuffer.append("</xml>");
return stringBuffer.toString();
}
}
下面贴EmptyUtils
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
public class EmptyUtils {
/**
* 判断对象是否为空
*
* @param obj 对象
* @return {@code true}: 为空<br>{@code false}: 不为空
*/
public static boolean isEmpty(Object obj) {
//判断对象是否为null
if (obj == null) {
return true;
}
//判断String类型对象是否为空
if (obj instanceof String && obj.toString().length() == 0) {
return true;
}
//判断数组对象是否为空
if (obj.getClass().isArray() && Array.getLength(obj) == 0) {
return true;
}
//判断集合对象是否为空
if (obj instanceof Collection && ((Collection) obj).isEmpty()) {
return true;
}
//判断Map对象是否为空
if (obj instanceof Map && ((Map) obj).isEmpty()) {
return true;
}
return false;
}
/**
* 判断对象是否非空
*
* @param obj 对象
* @return {@code true}: 非空<br>{@code false}: 空
*/
public static boolean isNotEmpty(Object obj) {
return !isEmpty(obj);
}
}
获取到了文本值和openid等其他关键信息后,我们可以正式开始回复了。
下面贴回复代码
public String replyContentMsg(String message) throws InstantiationException, IllegalAccessException {
ContentMsg contentMsg = receiveContentMsg(message);
final String receiveContentMsg = contentMsg.getContent();
if (receiveContentMsg.equals("我的openid")){
//回复内容 比方说获取自己的openid
contentMsg.setContent(contentMsg.getFromUserName());
}else{
contentMsg.setContent("我也不知道回什么好");
}
//注意 这边的 FromUserName 和 ToUserName 需要对调一下 因为现在是服务器发送给用户了
String temp = contentMsg.getFromUserName();
contentMsg.setFromUserName(contentMsg.getToUserName());
contentMsg.setToUserName(temp);
contentMsg.setMsgType("text");
contentMsg.setCreateTime(new Date().getTime()+"");
return XmlUtils.parseToXml(contentMsg);
}
}
效果展示
这样我们的消息回复就算完成了,其实可以写的简单 不需要建基础模型和反射做个简单的例子照样可以完成,但是我还是希望同学们能多学到一点。希望我们可以一起进步,因为我也只是个刚刚开始起步的年轻人。(想找个好工作hhh)