4.3.3 解析请求消息

当用户向公众账号发送消息时,微信服务器会将消息通过POST方式转发到在接口配置信息中填写的URL上。也就是说,接收用户消息与第3章中介绍的请求校验使用同一个URL。在3.2.4节中,我们创建了一个请求处理核心类CoreServlet,它的doGet()方法用于请求校验,而doPost()方法正是用于处理用户发送的消息。CoreServlet类的doPost方法定义如下:

  1. /**
  2. * 处理微信服务器发来的消息
  3. */
  4. public void doPost(HttpServletRequest request, HttpServletResponse response) throws
  5. ServletException, IOException {
  6. // TODO 消息的接收、处理和响应
  7. }

说明 在MyEclipse中,标记“//TODO”是一种特殊的注释,表示尚未完成的待办事项,用于提醒开发者还有特定的任务要完成,在Tasks视图中能够看到所有待办事项。Tasks视图所在位置:Window→Show View→Other→General→Tasks。


微信服务器发来的请求消息都被封装在request对象中,可以从request对象中取出请求中包含的数据。通常使用request对象的getParameter()方法获取请求中的参数,而在这里需要使用request对象的getInputStream()方法。首先通过request.getInputStream()获得输入流,再使用Dom4j读取输入流并解析XML格式的请求消息,最后将解析得到的消息参数放入HashMap中。我们将解析请求消息的过程封装成方法,代码如下:

  1. /**
  2. * 解析微信发来的请求(XML)
  3. *
  4. * @param request
  5. * @return Map<String, String>
  6. * @throws Exception
  7. */
  8. @SuppressWarnings("unchecked")
  9. public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
  10. // 将解析结果存储在HashMap中
  11. Map<String, String> map = new HashMap<String, String>();
  12.  
  13. // 从request中取得输入流
  14. InputStream inputStream = request.getInputStream();
  15. // 读取输入流
  16. SAXReader reader = new SAXReader();
  17. Document document = reader.read(inputStream);
  18. // 得到XML根元素
  19. Element root = document.getRootElement();
  20. // 得到根元素的所有子节点
  21. List<Element> elementList = root.elements();
  22.  
  23. // 遍历所有子节点
  24. for (Element e : elementList)
  25. map.put(e.getName(), e.getText());
  26.  
  27. // 释放资源
  28. inputStream.close();
  29. inputStream = null;
  30.  
  31. return map;
  32. }

当接收到用户发送的消息时,首先调用上面的parseXml()方法得到请求消息的参数集合,再取出消息类型MsgType判断用户发送的消息类型,最后取出消息的其他参数进行处理。主要代码如下:

  1. // 调用parseXml方法解析请求消息
  2. Map<String, String> requestMap = MessageUtil.parseXml(request);
  3. // 发送方账号
  4. String fromUserName = requestMap.get("FromUserName");
  5. // 开发者微信号
  6. String toUserName = requestMap.get("ToUserName");
  7. // 消息创建时间
  8. String createTime = requestMap.get("CreateTime");
  9. // 消息类型
  10. String msgType = requestMap.get("MsgType");
  11. // 消息ID
  12. String msgId = requestMap.get("MsgId");
  13.  
  14.  
  15. // 文本消息
  16. if (msgType.equals("text")) {
  17. // 消息内容
  18. String content = requestMap.get("Content");
  19. // TODO 处理文本消息请求
  20. }
  21. // 图片消息
  22. else if (msgType.equals("image")) {
  23. // 图片链接
  24. String picUrl = requestMap.get("PicUrl");
  25. // TODO 处理图片消息请求
  26. }
  27. // 语音消息
  28. else if (msgType.equals("voice")) {
  29. // 媒体文件标识
  30. String mediaId = requestMap.get("MediaId");
  31. // 文件格式
  32. String format = requestMap.get("Format");
  33. // TODO 处理语音消息请求
  34. }
  35. // 视频消息
  36. else if (msgType.equals("video")) {
  37. // 媒体文件标识
  38. String mediaId = requestMap.get("MediaId");
  39. // 视频消息缩略图的媒体ID
  40. String thumbMediaId = requestMap.get("ThumbMediaId");
  41. // TODO 处理视频消息请求
  42. }
  43. // 地理位置消息
  44. else if (msgType.equals("location")) {
  45. // 地理位置纬度
  46. String lat = requestMap.get("Location_X");
  47. // 地理位置经度
  48. String lng = requestMap.get("Location_Y");
  49. // 地理位置信息
  50. String label = requestMap.get("Label");
  51. // 地图缩放大小
  52. String scale = requestMap.get("Scale");
  53. // TODO 处理地理位置消息请求
  54. }
  55. // 链接消息
  56. else if (msgType.equals("link")) {
  57. // 消息标题
  58. String title = requestMap.get("Title");
  59. // 消息描述
  60. String description = requestMap.get("Description");
  61. // 消息链接
  62. String url = requestMap.get("Url");
  63. // TODO 处理链接消息请求
  64. }

代码开始处,与参数MsgType一并取出的还有FromUserName、ToUserName、CreateTime和MsgId4个参数,因为不管用户发送的是哪种类型的消息,都会包含这5个参数。而其他参数就需要根据消息类型进行获取,如文本消息的Content参数。