WeChat public number development message processing

Keywords: xml Java REST encoding

I. Preface

WeChat public number development (1) WeChat access certification as developer

This article will implement
  1. receive messages
  2. Reply message

2, Message reception

Message receiving POST and wechat authentication GET are the same interface (URL filled in by the developer)

@Slf4j
@RestController
@RequestMapping("/api/weixin/index")
@Api(tags = "WeChat - Interface")
public class IndexController extends BaseController {

    /**
     * Parse request message, post request
     */
    @PostMapping
    public void msgProcess(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // Get the requested byte stream
        ServletInputStream inputStream = request.getInputStream();
        // Convert to character stream, get buffer stream
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
        BufferedReader reader = new BufferedReader(inputStreamReader);
        String content = null;
        // Read message content
        while ((content = reader.readLine()) != null) {
            System.out.println(content);
        }
    }

}

The format of the received text message is as follows

For other message formats, you can view wechat open documents: https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html

<xml>
	<ToUserName><![CDATA[gh_683c79d81f78]]></ToUserName>
	<FromUserName><![CDATA[Wy5wvH9zSaQZSZcV7wZE18D]]></FromUserName>
	<CreateTime>1578996487</CreateTime>
	<MsgType><![CDATA[text]]></MsgType>
	<Content><![CDATA[test]]></Content>
	<MsgId>22605792697428179</MsgId>
</xml>
parameter describe
ToUserName Developer wechat
FromUserName Sender account number (an OpenID)
CreateTime Message creation time (integer)
MsgType Message type, text
Content Text message content
MsgId Message id, 64 bit integer

3, Message processing

When a user sends a message to the public number (or when an event is triggered by some specific user operation), a POST request is generated. The developer can respond to the message by returning a specific XML structure in the response package (Get).

Warm tip: the message encapsulation here can be based on Wechat open document The message format given in the self encapsulation, the complete code can refer to the source code given at the end of the article Oh~

1. Encapsulate message type enumeration class

① Request message type
public enum EnumRequestMessageType {
    // Request message type
    TEXT("text", "text"),
    IMAGE("image", "picture"),
    VOICE("voice", "Voice"),
    VIDEO("video", "video"),
    SHORTVIDEO("shortvideo", "Small video"),
    LOCATION("location", "geographical position"),
    LINK("link", "link"),
    // Event push
    EVENT("event", "Push");
}
② Event push type
public enum EnumEventMessageType {
    SUBSCRIBE("subscribe", "Subscribe"),
    UNSUBSCRIBE("unsubscribe", "Cancel subscription"),
    CLICK("CLICK", "Custom menu click event"),
    SCAN("SCAN", "Event push when the user has followed");
}
③ Response message type
public enum EnumResponseMessageType {
    TEXT("text", "text"),
    IMAGE("image", "picture"),
    VOICE("voice", "Voice"),
    VIDEO("video", "video"),
    MUSIC("music", "Music"),
    NEWS("news", "Image & Text");
}

2. Encapsulate request message

① Request message base class
@Data
@ApiModel(description = "Request message-Base class")
public class BaseMessageDTO {

    @ApiModelProperty(value = "Developer wechat")
    private String ToUserName;

    @ApiModelProperty(value = "Sender account number (one OpenID)")
    private String FromUserName;

    @ApiModelProperty(value = "Message creation time (integer)")
    private long CreateTime;

    @ApiModelProperty(value = "news id,64 short")
    private long MsgId;

}
② Event push
@Data
@ApiModel(description = "Event push")
public class EventMessageDTO extends BaseMessageDTO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumRequestMessageType.EVENT.getType();

    @ApiModelProperty(value = "Text message content")
    private String Event;

}
③ Picture message
@Data
@ApiModel(description = "Picture message")
public class ImageMessageDTO extends BaseMessageDTO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumRequestMessageType.IMAGE.getType();

    @ApiModelProperty(value = "Picture link (generated by the system)")
    private String PicUrl;

    @ApiModelProperty(value = "Picture message media id,Can call to get temporary material interface to pull data")
    private String MediaId;

}
④ Link message
@Data
@ApiModel(description = "Link message")
public class LinkMessageDTO extends BaseMessageDTO {

	@ApiModelProperty(value = "Message type", hidden = true)
	private String MsgType = EnumRequestMessageType.LINK.getType();

	@ApiModelProperty(value = "Message header")
	private String Title;

	@ApiModelProperty(value = "Message description")
	private String Description;

	@ApiModelProperty(value = "Message link")
	private String Url;

}
⑤ Geolocation message
@Data
@ApiModel(description = "Geolocation message")
public class LocationMessageDTO extends BaseMessageDTO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumRequestMessageType.LOCATION.getType();

    @ApiModelProperty(value = "Geographic location dimension")
    private String Location_X;

    @ApiModelProperty(value = "Geographic location longitude")
    private String Location_Y;

    @ApiModelProperty(value = "Map zoom size")
    private String Scale;

    @ApiModelProperty(value = "Geographic location information")
    private String Label;

}
⑥ Small video message
@Data
@ApiModel(description = "Small video message")
public class ShortVideoMessageDTO extends BaseMessageDTO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumRequestMessageType.SHORTVIDEO.getType();

    @ApiModelProperty(value = "Video message media id,Can call to get temporary material interface to pull data")
    private String MediaId;

    @ApiModelProperty(value = "Media for video message thumbnails id,Can call to get temporary material interface to pull data")
    private String ThumbMediaId;

}
⑦ Text message
@Data
@ApiModel(description = "Text message")
public class TextMessageDTO extends BaseMessageDTO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumRequestMessageType.TEXT.getType();

    @ApiModelProperty(value = "Text message content")
    private String Content;

}
⑧ Video message
@Data
@ApiModel(description = "Video message")
public class VideoMessageDTO extends BaseMessageDTO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumRequestMessageType.VIDEO.getType();

    @ApiModelProperty(value = "Video message media id,Can call to get temporary material interface to pull data")
    private String MediaId;

    @ApiModelProperty(value = "Media for video message thumbnails id,You can call the multimedia file download interface to pull data")
    private String ThumbMediaId;

}
⑨ Voice message
@Data
@ApiModel(description = "Voice message")
public class VoiceMessageDTO extends BaseMessageDTO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumRequestMessageType.VOICE.getType();

    @ApiModelProperty(value = "Voice message media id,Can call to get temporary material interface to pull data")
    private String MediaId;

    @ApiModelProperty(value = "Voice formats, such as amr,speex etc.")
    private String Format;

    /**
     * Please note that after opening speech recognition, WeChat will send the voice to the public number every time, and the WeChat will push the voice message XML packet.
     * Add a Recognition field (Note: due to the client cache, the developer can turn on or off the voice Recognition function, which will take effect immediately for new followers and 24 hours for users who have already paid attention. Developers can refocus on this account for testing)
     */
    @ApiModelProperty(value = "Speech recognition results, UTF8 Code")
    private String Recognition;

}

3. Encapsulate response messages

① Response message base class
@Data
@ApiModel(description = "Response message-Base class")
public class BaseMessageVO {

    @ApiModelProperty(value = "Receiver account number (received OpenID)")
    private String ToUserName;

    @ApiModelProperty(value = "Developer wechat")
    private String FromUserName;

    @ApiModelProperty(value = "Message creation time (integer)")
    private long CreateTime;

    @ApiModelProperty(value = "Position 0 x0001 When marked, the message just received by the star sign")
    private int FuncFlag = 0;

}
② Picture message
@Data
@ApiModel(description = "Picture message")
public class ImageMessageVO extends BaseMessageVO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumResponseMessageType.IMAGE.getType();

    @ApiModelProperty(value = "picture")
    private Image Image;

    @Data
    @AllArgsConstructor
    @ApiModel(description = "In picture message Image Class definition")
    public static class Image {

        @ApiModelProperty(value = "By uploading multimedia files through the interface in material management, the id")
        private String MediaId;

    }

}
③ Music News
@Data
@ApiModel(description = "information about the music")
public class MusicMessageVO extends BaseMessageVO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumResponseMessageType.MUSIC.getType();

    @ApiModelProperty(value = "Music")
    private Music Music;

    @Data
    @AllArgsConstructor
    @ApiModel(description = "Music News Music Class definition")
    public static class Music {

        @ApiModelProperty(value = "Music title")
        private String Title;

        @ApiModelProperty(value = "Music description")
        private String Description;

        @ApiModelProperty(value = "Music links")
        private String MusicUrl;

        @ApiModelProperty(value = "High quality music links, WIFI Environment has priority to use this link to play music")
        private String HQMusicUrl;

        @ApiModelProperty(value = "Media for thumbnails id,By uploading multimedia files through the interface in material management, the id")
        private String ThumbMediaId;

    }

}
④ Graphic message
@Data
@ApiModel(description = "Graphic message")
public class NewsMessageVO extends BaseMessageVO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumResponseMessageType.NEWS.getType();

    @ApiModelProperty(value = "The number of graphic messages; when the user sends text, picture, video, graphic and geographic location messages, the developer can only reply one graphic message; the rest scenarios can reply up to eight graphic messages")
    private int ArticleCount = 0;

    @ApiModelProperty(value = "Note: if the number of pictures exceeds the limit, only the number within the limit will be sent")
    private List<Article> Articles;

    @Data
    @ApiModel(description = "Text message Article Class definition")
    public static class Article {

        @ApiModelProperty(value = "Text message title")
        private String Title;

        @ApiModelProperty(value = "Text message description")
        private String Description = "";

        @ApiModelProperty(value = "Picture links, support JPG,PNG Format, better effect is big picture 360*200,Small figure 200*200")
        private String PicUrl = "";

        @ApiModelProperty(value = "Click the graphic message jump link")
        private String Url = "";

    }

    public void addArticle(Article article) {
        if (Articles == null) {
            Articles = Lists.newLinkedList();
        }
        Articles.add(article);
        ArticleCount++;
    }

}
⑤ Text message
@Data
@ApiModel(description = "Text message")
public class TextMessageVO extends BaseMessageVO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumResponseMessageType.TEXT.getType();

    @ApiModelProperty(value = "Message content of the reply (line feed: in content The wechat client supports line feed display when line feed is enabled in)")
    private String Content;

}
⑥ Video message
@Data
@ApiModel(description = "Video message")
public class VideoMessageVO extends BaseMessageVO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumResponseMessageType.VIDEO.getType();

    @ApiModelProperty(value = "video")
    private Video Video;

    @Data
    @AllArgsConstructor
    @ApiModel(description = "In video message Voice Class definition")
    public static class Video {

        @ApiModelProperty(value = "By uploading multimedia files through the interface in material management, the id")
        private String MediaId;

        @ApiModelProperty(value = "Title of video message")
        private String Title;

        @ApiModelProperty(value = "Description of video message")
        private String Description;

    }

}
⑦ Voice message
@Data
@ApiModel(description = "Voice message")
public class VoiceMessageVO extends BaseMessageVO {

    @ApiModelProperty(value = "Message type", hidden = true)
    private String MsgType = EnumResponseMessageType.VOICE.getType();

    @ApiModelProperty(value = "Voice")
    private Voice Voice;

    @Data
    @AllArgsConstructor
    @ApiModel(description = "In voice message Voice Class definition")
    public static class Voice {

        @ApiModelProperty(value = "By uploading multimedia files through the interface in material management, the id")
        private String MediaId;

    }

}

4. Message processing tool class

① Introducing required dependencies in pom.xml
<!-- dom4j:  Analyze the request from wechat( XML) -->
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jaxen/jaxen -->
<dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.1.6</version>
</dependency>

<!-- xstream: Convert response messages to xml Return -->
<!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.11.1</version>
</dependency>
② Tools
  1. Parsing the request from wechat (xml)
  2. Converting Java objects that respond to messages to xml
public class MessageUtil {

    /**
     * Parsing the request from wechat (XML)
     *
     * @param request
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
        // Store the resolution results in the HashMap
        Map<String, String> map = new HashMap<>(10);

        // Get input stream from request
        InputStream inputStream = request.getInputStream();
        // Read input stream
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // Get the xml root element
        Element root = document.getRootElement();
        // Get all the children of the root element
        List<Element> elementList = root.elements();

        // Traverse all child nodes
        for (Element e : elementList) {
            map.put(e.getName(), e.getText());
        }

        // Release resources
        inputStream.close();
        inputStream = null;
        return map;
    }

    /**
     * Converting text message objects to xml
     *
     * @param textMessageVO The text message object XStream is a tool for converting Java objects and XML
     * @return xml
     */
    public static String textMessageToXml(TextMessageVO textMessageVO) {
        xstream.alias("xml", textMessageVO.getClass());
        return xstream.toXML(textMessageVO);
    }

    /**
     * Image message object to xml
     *
     * @param imageMessageVO Picture message object
     * @return xml
     */
    public static String imageMessageToXml(ImageMessageVO imageMessageVO) {
        xstream.alias("xml", imageMessageVO.getClass());
        return xstream.toXML(imageMessageVO);
    }

    /**
     * Voice message object to xml
     *
     * @param voiceMessageVO Voice message object
     * @return xml
     */
    public static String voiceMessageToXml(VoiceMessageVO voiceMessageVO) {
        xstream.alias("xml", voiceMessageVO.getClass());
        return xstream.toXML(voiceMessageVO);
    }

    /**
     * Converting video message objects to xml
     *
     * @param videoMessageVO Video message object
     * @return xml
     */
    public static String videoMessageToXml(VideoMessageVO videoMessageVO) {
        xstream.alias("xml", videoMessageVO.getClass());
        return xstream.toXML(videoMessageVO);
    }

    /**
     * Converting music message objects to xml
     *
     * @param musicMessage Music message object
     * @return xml
     */
    public static String musicMessageToXml(MusicMessageVO musicMessage) {
        xstream.alias("xml", musicMessage.getClass());
        return xstream.toXML(musicMessage);
    }

    /**
     * Converting text message objects to xml
     *
     * @param newsMessage Message object
     * @return xml
     */
    public static String newsMessageToXml(NewsMessageVO newsMessage) {
        xstream.alias("xml", newsMessage.getClass());
        xstream.alias("item", NewsMessageVO.Article.class);
        return xstream.toXML(newsMessage);
    }

    /**
     * Extend xstream to support CDATA blocks
     */
    private static XStream xstream = new XStream(new XppDriver() {
        @Override
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                // Add CDATA tags to all xml node transformations
                boolean cdata = true;

                @Override
                @SuppressWarnings("unchecked")
                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }

                @Override
                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });

}

4, Reply to a message

1. Reply to text message

@PostMapping
public void msgProcess(HttpServletRequest request, HttpServletResponse response) throws Exception {
     // Set the encoding of request and response to UTF-8 (to prevent Chinese scrambling)
     request.setCharacterEncoding("UTF-8");
     response.setCharacterEncoding("UTF-8");
     // Call core business class to receive and process messages
     String respMessage = msgService.processRequest(request);
     // Response message
     PrintWriter out = response.getWriter();
     out.print(respMessage);
     out.close();
 }

The logic of the processRequest message processing method is as follows

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class MsgServiceImpl implements IMsgService {

    @Override
    public String processRequest(HttpServletRequest request) {
        String respMessage = null;
        try {
            // xml request parsing
            Map<String, String> requestMap = MessageUtil.parseXml(request);

            // Sender account number (open ID)
            String fromUserName = requestMap.get("FromUserName");
            // public account
            String toUserName = requestMap.get("ToUserName");
            // Message type
            String msgType = requestMap.get("MsgType");

            // Reply to text message
            TextMessageVO textMessage = new TextMessageVO();
            textMessage.setToUserName(fromUserName);
            textMessage.setFromUserName(toUserName);
            textMessage.setCreateTime(System.currentTimeMillis());
            textMessage.setMsgType(EnumResponseMessageType.TEXT.getType());

            // Default returned text message content
            String respContent = "Request processing exception, please try again later!";

            // Event push
            if (EnumRequestMessageType.getEnum(msgType) == EnumRequestMessageType.EVENT) {
                // Event type
                String eventType = requestMap.get("Event");
                switch (EnumEventMessageType.getEnum(eventType)) {
                    // Subscribe
                    case SUBSCRIBE:
                        respContent = "Dear, thank you for your attention!";
                        break;
                    // Cancel subscription
                    case UNSUBSCRIBE:
                        // After TODO unsubscribes, the user can no longer receive the message sent by the public number, so there is no need to reply to the message.
                        break;
                    // Custom menu click event
                    case CLICK:
                        // TODO custom menu right is not open, do not process this kind of message temporarily
                        break;
                    default:
                        break;
                }
            } else {
                respContent = EnumRequestMessageType.getEnum(msgType).getTypeValue();
            }
            textMessage.setContent(respContent);
            respMessage = MessageUtil.textMessageToXml(textMessage);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return respMessage;
    }
    
}

test

2. Return message content as is

Return the message content as is, and call processrequestreturnsamsg method in IMsgService

public String processRequestReturnSameMsg(HttpServletRequest request) {
    String respMessage = null;
    try {
        // xml request parsing
        Map<String, String> requestMap = MessageUtil.parseXml(request);

        // Sender account number (open ID)
        String fromUserName = requestMap.get("FromUserName");
        // public account
        String toUserName = requestMap.get("ToUserName");
        // Message type
        String msgType = requestMap.get("MsgType");

        // Reply message
        BaseMessageVO baseMessageVO = new BaseMessageVO();
        baseMessageVO.setToUserName(fromUserName);
        baseMessageVO.setFromUserName(toUserName);
        baseMessageVO.setCreateTime(System.currentTimeMillis());

        switch (EnumRequestMessageType.getEnum(msgType)) {
            // Text message
            case TEXT:
                TextMessageVO textMessageVO = BeanUtil.copyProperties(baseMessageVO, TextMessageVO.class);
                textMessageVO.setContent(requestMap.get("Content"));
                respMessage = MessageUtil.textMessageToXml(textMessageVO);
                break;
            // Picture message
            case IMAGE:
                ImageMessageVO imageMessageVO = BeanUtil.copyProperties(baseMessageVO, ImageMessageVO.class);
                ImageMessageVO.Image image = new ImageMessageVO.Image(requestMap.get("MediaId"));
                imageMessageVO.setImage(image);
                respMessage = MessageUtil.imageMessageToXml(imageMessageVO);
                break;
            // Voice message
            case VOICE:
                VoiceMessageVO voiceMessageVO = BeanUtil.copyProperties(baseMessageVO, VoiceMessageVO.class);
                VoiceMessageVO.Voice voice = new VoiceMessageVO.Voice(requestMap.get("MediaId"));
                voiceMessageVO.setVoice(voice);
                respMessage = MessageUtil.voiceMessageToXml(voiceMessageVO);
                break;
            // Event push
            case EVENT:
                // Event type
                String eventType = requestMap.get("Event");
                switch (EnumEventMessageType.getEnum(eventType)) {
                    // Subscribe
                    case SUBSCRIBE:
                        NewsMessageVO newsMessage = BeanUtil.copyProperties(baseMessageVO, NewsMessageVO.class);
                        NewsMessageVO.Article article = new NewsMessageVO.Article();
                        article.setTitle("Thank you for your attention, this is a graphic message!");
                        article.setDescription("mysql turn oracle note");
                        article.setPicUrl("https://mmbiz.qpic.cn/mmbiz_png/iaUVVC0premhqE0TrtLzM6ABMIKKjnu81hraZvXia52byYMqADCyqXKwbs2wJ6jiadWc7MypLKL4EC5mUzXZKH2Rg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1");
                        article.setUrl("https://mp.weixin.qq.com/s?__biz=Mzg2NzEwMjc3Ng==&mid=2247483813&idx=1&sn=7a18081426d014ddccd203d33011f526&chksm=ce41f8f2f93671e4fc5d93292360fd24da4cf1434415befc37f25be2d40780b4a485653861d8&mpshare=1&scene=1&srcid=&sharer_sharetime=1579140222251&sharer_shareid=936076bf8d5bee83e89fd7e769b5c6db&key=c62bc26d01d4cb91d588b8abdeaca0fbba6d713fbc52e3b4c4a9f0377e231e0fe6b4ce07f287f509e37cefa17a0346475f12d85e21bcdbb8e953d0685018a874fbd80005417e94836ad9b0ff7559b334&ascene=1&uin=MTg4MzA0MzMxNA%3D%3D&devicetype=Windows+7&version=62070158&lang=zh_CN&exportkey=AYq%2FJJZv5hRr7YLluyVInZk%3D&pass_ticket=vCPgwidZSOs1xBfcd5SrzkCdVlApSWF7Xc%2BOzjYf8GlJ9%2BLQco9XYzTHe9yWHqc1");
                        newsMessage.addArticle(article);
                        respMessage = MessageUtil.newsMessageToXml(newsMessage);
                        break;
                    // Cancel subscription
                    case UNSUBSCRIBE:
                        // After TODO unsubscribes, the user can no longer receive the message sent by the public number, so there is no need to reply to the message.
                        break;
                    // Custom menu click event
                    case CLICK:
                        // TODO custom menu right is not open, do not process this kind of message temporarily
                        break;
                    default:
                        break;
                }
                break;
            default:
                break;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return respMessage;
}

test

This is a simple way to receive and reply messages. We can learn about message processing according to the official documents provided by wechat~

demo source code of this case

https://gitee.com/zhengqingya/java-workspace

559 original articles published, 583 praised, 1.02 million visitors+
His message board follow

Posted by vornn on Fri, 17 Jan 2020 00:21:30 -0800