4.3.3 解析请求消息
当用户向公众账号发送消息时,微信服务器会将消息通过POST方式转发到在接口配置信息中填写的URL上。也就是说,接收用户消息与第3章中介绍的请求校验使用同一个URL。在3.2.4节中,我们创建了一个请求处理核心类CoreServlet,它的doGet()方法用于请求校验,而doPost()方法正是用于处理用户发送的消息。CoreServlet类的doPost方法定义如下:
- /**
- * 处理微信服务器发来的消息
- */
- public void doPost(HttpServletRequest request, HttpServletResponse response) throws
- ServletException, IOException {
- // TODO 消息的接收、处理和响应
- }
说明 在MyEclipse中,标记“//TODO”是一种特殊的注释,表示尚未完成的待办事项,用于提醒开发者还有特定的任务要完成,在Tasks视图中能够看到所有待办事项。Tasks视图所在位置:Window→Show View→Other→General→Tasks。
微信服务器发来的请求消息都被封装在request对象中,可以从request对象中取出请求中包含的数据。通常使用request对象的getParameter()方法获取请求中的参数,而在这里需要使用request对象的getInputStream()方法。首先通过request.getInputStream()获得输入流,再使用Dom4j读取输入流并解析XML格式的请求消息,最后将解析得到的消息参数放入HashMap中。我们将解析请求消息的过程封装成方法,代码如下:
- /**
- * 解析微信发来的请求(XML)
- *
- * @param request
- * @return Map<String, String>
- * @throws Exception
- */
- @SuppressWarnings("unchecked")
- public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
- // 将解析结果存储在HashMap中
- Map<String, String> map = new HashMap<String, String>();
- // 从request中取得输入流
- InputStream inputStream = request.getInputStream();
- // 读取输入流
- SAXReader reader = new SAXReader();
- Document document = reader.read(inputStream);
- // 得到XML根元素
- Element root = document.getRootElement();
- // 得到根元素的所有子节点
- List<Element> elementList = root.elements();
- // 遍历所有子节点
- for (Element e : elementList)
- map.put(e.getName(), e.getText());
- // 释放资源
- inputStream.close();
- inputStream = null;
- return map;
- }
当接收到用户发送的消息时,首先调用上面的parseXml()方法得到请求消息的参数集合,再取出消息类型MsgType判断用户发送的消息类型,最后取出消息的其他参数进行处理。主要代码如下:
- // 调用parseXml方法解析请求消息
- Map<String, String> requestMap = MessageUtil.parseXml(request);
- // 发送方账号
- String fromUserName = requestMap.get("FromUserName");
- // 开发者微信号
- String toUserName = requestMap.get("ToUserName");
- // 消息创建时间
- String createTime = requestMap.get("CreateTime");
- // 消息类型
- String msgType = requestMap.get("MsgType");
- // 消息ID
- String msgId = requestMap.get("MsgId");
- // 文本消息
- if (msgType.equals("text")) {
- // 消息内容
- String content = requestMap.get("Content");
- // TODO 处理文本消息请求
- }
- // 图片消息
- else if (msgType.equals("image")) {
- // 图片链接
- String picUrl = requestMap.get("PicUrl");
- // TODO 处理图片消息请求
- }
- // 语音消息
- else if (msgType.equals("voice")) {
- // 媒体文件标识
- String mediaId = requestMap.get("MediaId");
- // 文件格式
- String format = requestMap.get("Format");
- // TODO 处理语音消息请求
- }
- // 视频消息
- else if (msgType.equals("video")) {
- // 媒体文件标识
- String mediaId = requestMap.get("MediaId");
- // 视频消息缩略图的媒体ID
- String thumbMediaId = requestMap.get("ThumbMediaId");
- // TODO 处理视频消息请求
- }
- // 地理位置消息
- else if (msgType.equals("location")) {
- // 地理位置纬度
- String lat = requestMap.get("Location_X");
- // 地理位置经度
- String lng = requestMap.get("Location_Y");
- // 地理位置信息
- String label = requestMap.get("Label");
- // 地图缩放大小
- String scale = requestMap.get("Scale");
- // TODO 处理地理位置消息请求
- }
- // 链接消息
- else if (msgType.equals("link")) {
- // 消息标题
- String title = requestMap.get("Title");
- // 消息描述
- String description = requestMap.get("Description");
- // 消息链接
- String url = requestMap.get("Url");
- // TODO 处理链接消息请求
- }
代码开始处,与参数MsgType一并取出的还有FromUserName、ToUserName、CreateTime和MsgId4个参数,因为不管用户发送的是哪种类型的消息,都会包含这5个参数。而其他参数就需要根据消息类型进行获取,如文本消息的Content参数。