公衆號開發精品教程(2)——將項目接入微信及簡單交互

系列文章傳送門

公衆號開發精品教程(1)——緒論及環境搭建

公衆號開發精品教程(2)——將項目接入微信及簡單交互

公衆號開發精品教程(3)——創建菜單

公衆號開發精品教程(4)——生成帶參數的二維碼及合成海報

公衆號開發精品教程(5)——獲取用戶基本信息與網頁授權

整個項目的源碼已經上傳到百度網盤(博主的Git在維護,就不拿出來丟人了),永久有效,免費,在ChatConf類中填寫自己的APPID和開發者密鑰,在相關地方替換一下外網域名,即可使用,如有任何問題,歡迎在下方評論:

鏈接:百度網盤傳送門
提取碼:03eb

目錄

 

前言

開始接入

簡單的交互

一、被關注時自動回覆

二、用戶發送消息時,自動回覆

小結


前言

在上一篇博文中,我們申請了公衆號的測試號,創建了服務器項目,並且通過內網穿透映射到了外網上。

那麼我們如何讓微信把公衆號產生的消息,發送給我們的服務器呢?這就要在公衆平臺去配置服務器信息,因爲我們這裏是自己學習體驗,所以在公衆號測試系統去操作即可,公衆號測試系統入口:

https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

用你註冊公衆號的微信,掃碼登錄即可。

開始接入

登錄到測試號系統中後,你會看到如下信息:

我們就是要在這裏,配置自己的服務器信息。我們暫且不配置,因爲後臺服務器還沒有進行相關代碼的編寫,先看一下文檔怎麼說。

 

主要是三個信息

1、提交配置之後,用戶在你的公衆號所有的操作,微信都會轉發到你填寫的URL

2、成爲開發者時的認證,是以GET方式提交到你填寫的URL,並且攜帶4個參數

3、認證邏輯你自己編寫,只要你最後返回給我echostr字符串即可,哪怕不進行驗證直接返回

既然這樣,那我們就開始動手吧!

仍然繼續在上一篇文章中創建的項目,我們新建一個WechatController,用於處理微信發來的請求:

package com.blog.wechat.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 *  微信控制器,負責接收微信推送的消息及開發者驗證
 * @author 秋楓豔夢
 * @date 2019-06-02
 * */
@Controller
public class WechatController {

    /**
     *  進行開發者驗證,接入微信。注意這個方法是GET請求
     * @param signature 微信加密簽名
     * @param timestamp 時間戳
     * @param nonce 隨機數
     * @param echostr 隨機字符串
     * @return 驗證通過,原樣返回echostr字符串
     * */
    @RequestMapping(value = "/wechat",method = RequestMethod.GET)
    public void checkAuth(@RequestParam(value = "signature") String signature,
                          @RequestParam(value = "timestamp") String timestamp,
                          @RequestParam(value = "nonce") String nonce,
                          @RequestParam(value = "echostr") String echostr,
                          HttpServletResponse response) {
        //認證邏輯這裏就不實現了,我們直接返回echostr,寫入到響應體中
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            //返回結果
            writer.print(echostr);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (writer!=null){
                writer.close();
            }
        }
    }
}

然後在測試號系統中填寫我們的服務器配置(不要忘了先將你的項目映射到外網上),域名+請求路徑:

 然後點擊提交,如果你的項目及配置沒問題的話,會顯示配置成功,這時就已經對接上了,用戶跟公衆號產生的所有交互都會訪問這個URL。

簡單的交互

測試號系統中有一個二維碼,我們掃一下,關注一下我們的測試公衆號:

我們向公衆號發起了交互,卻說服務異常? 這是因爲我們的服務器沒有作出響應。前面提到,一旦成爲了開發者模式,所有的交互行爲都會訪問我們填寫的URL,這裏也就是http://umu5uk.natappfree.cc/wechat,但是我們並沒有處理這些交互,微信得不到響應,就會通知用戶發生了異常。

那麼問題來了,我只有一個/wechat請求處理路徑,而且我已經用這個路徑做了開發者認證,怎麼能繼續用它處理交互行爲呢?

這就需要看文檔了,文檔指出,成爲開發者模式的時候,是GET方式訪問這個URL,而所有的事件交互,以POST方式訪問。而且交互分爲幾種:普通消息、事件推送等,剛纔我們向公衆號發信息,屬於普通消息,而被關注屬於事件推送。

接下來,我們做兩件事:

1、被關注的時候,給用戶發送消息;

2、用戶發送消息過來時,回覆用戶;

一、被關注時自動回覆

這裏需要向用戶發送信息,所以先看一下文檔怎麼說:

因爲我們要在用戶關注的時候,發送給用戶一段文本信息,所以就要先接收微信的事件推送,繼續看文檔:

 文檔指出,當公衆號被關注時,微信會向我們配置的服務器發送一個POST請求,並且攜帶一個XML格式的請求體,結合下面的參數說明,我們可以知道被關注時的event爲subscribe,那麼思路就理順了:

先在控制器中解析微信推過來的xml數據,拿到event的值,如果是subscribe,則返回一個回覆文本消息的xml響應體。

然後開始寫代碼,因爲微信推過來的數據是XML格式的,操作起來不方便,所以我們先將它轉爲Map,寫一個工具類:

注意:這裏是採用dom4j解析的,需要引入依賴。

package com.blog.wechat.utils;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  XML解析工具類
 * @author 秋楓豔夢
 * @date 2019-06-02
 * */
public class XMLUtil {

    /**
     *  將XML轉換成Map
     * @param inputStream 請求體中的輸入流
     * @return Map
     * */
    public static Map<String,String> getMap(InputStream inputStream){
        //返回的Map
        Map<String,String> map = new HashMap<>();
        //初始化解析器
        SAXReader reader = new SAXReader();

        //讀取XML文檔
        Document document = null;
        try {
            document = reader.read(inputStream);
        } catch (DocumentException e) {
            e.printStackTrace();
        }

        //獲取XML文檔的根節點
        Element element = document.getRootElement();

        //遍歷所有節點,把鍵和值存入Map
        List<Element> elementList = element.elements();
        for (Element e : elementList) {
            map.put(e.getName(),e.getText());
        }

        return map;
    }
}

然後還需要一個工具類,負責生成回覆給用戶的消息:

package com.blog.wechat.utils;

/**
 *  返回消息的工具類
 * @author 秋楓豔夢
 * @date 2019-06-02
 * */
public class MessageUtil {
    /**
     * 要回復的消息
     * @param fromUser 發送方
     * @param toUser 接收方
     * @param content 回覆給用戶的內容
     * @return 整理好的XML文本
     * */
    public static String setMessage(String fromUser,String toUser,String content){

        return "<xml>\n" +
                "  <ToUserName><![CDATA["+toUser+"]]></ToUserName>\n" +
                "  <FromUserName><![CDATA["+fromUser+"]]></FromUserName>\n" +
                "  <CreateTime>12345678</CreateTime>\n" +
                "  <MsgType><![CDATA[text]]></MsgType>\n" +
                "  <Content><![CDATA["+content+"]]></Content>\n" +
                "</xml>";
    }
}

然後在WechatController中增加一個POST請求的處理方法,請求路徑依然是/wechat,返回數據格式是application/xml:

/**
     *  處理交互行爲
     * @param request 請求體
     * @param response 響應體
     * */
    @RequestMapping(value = "/wechat",method = RequestMethod.POST,produces = {"application/xml;charset=utf-8"})
    public void doRequest(HttpServletRequest request,HttpServletResponse response) throws IOException {
        //將XML轉爲Map
        Map<String,String> map = XMLUtil.getMap(request.getInputStream());
        PrintWriter writer = response.getWriter();

        //這裏不要弄混了,微信推過來的信息是用戶發過來的,所以ToUserName是我們的公衆號,FromUserName是用戶的微信openid
        //所以我們既然要回復過去,就要顛倒過來
        String fromUser = map.get("ToUserName");
        String toUser = map.get("FromUserName");
        String content = "";

        //先判斷是事件消息,還是普通消息
        if (map.get("MsgType").equals("event")){
            //如果是被關注事件,向用戶回覆內容,只需要將整理好的XML文本參數返回給微信即可
            if (map.get("Event").equals("subscribe")){
                content = "歡迎關注秋楓豔夢的測試公衆號!";
                //把數據包返回給微信服務器,微信服務器再推給用戶
                writer.print(MessageUtil.setMessage(fromUser,toUser,content));
            }
        }
        writer.close();
    }

然後重新運行項目,重新關注測試公衆號:

 到這裏,我們的第一個任務就完成了,接下來我們再完成下一個。

二、用戶發送消息時,自動回覆

這個場景很常見,用戶發送一個信息,公衆號返回一段內容,接下來我們就實現這個功能。

首先要明確,這個場景不再是事件消息了,而是普通消息,而且是普通消息中的文本消息,看一下文檔說明:

 所以我們要做如下處理:

/**
     *  處理交互行爲
     * @param request 請求體
     * @param response 響應體
     * */
    @RequestMapping(value = "/wechat",method = RequestMethod.POST,produces = {"application/xml;charset=utf-8"})
    public void doRequest(HttpServletRequest request,HttpServletResponse response) throws IOException {
        //將XML轉爲Map
        Map<String,String> map = XMLUtil.getMap(request.getInputStream());
        PrintWriter writer = response.getWriter();

        //這裏不要弄混了,微信推過來的信息是用戶發過來的,所以ToUserName是我們的公衆號,FromUserName是用戶的微信openid
        //所以我們既然要回復過去,就要顛倒過來
        String fromUser = map.get("ToUserName");
        String toUser = map.get("FromUserName");
        //要返回給用戶的信息
        String content = "";

        //先判斷是事件消息,還是普通消息
        if (map.get("MsgType").equals("event")){
            //如果是被關注事件,向用戶回覆內容,只需要將整理好的XML文本參數返回給微信即可
            if (map.get("Event").equals("subscribe")){
                content = "歡迎關注秋楓豔夢的測試公衆號!";
            }
        }else if (map.get("MsgType").equals("text")){
            //如果是普通文本消息,先拿到用戶發送過來的內容,模擬自動答疑的場景
            String text = map.get("Content");

            if (text.equals("1")){
                content = "您可以在“我的賬戶——服務——退款”中查看您的退款明細";
            }else if (text.equals("2")){
                content = "如果您購買了本店的產品,訂單頁面會展示在您的主菜單中";
            }else if (text.equals("3")){
                content = "如有更多問題,請撥打我們的客服熱線:xxxxx";
            }else {
                //否則,不管用戶輸入什麼,都返回給ta這個列表,這也是最常見的場景
                content = "請輸入您遇到的問題編號:\n"+
                        "1、如何查看退款進度?\n"+
                        "2、我的訂單在哪裏查看?\n"+
                        "3、其他問題";
            }
        }

        //把數據包返回給微信服務器,微信服務器再推給用戶
        writer.print(MessageUtil.setMessage(fromUser,toUser,content));
        writer.close();
    }

我們重新運行項目,測試一下:

 

小結

今天的博文就更新到這裏,下一篇文章,將帶領大家創建公衆號的菜單!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章