天天看點

微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

說明:該篇部落格是部落客一字一碼編寫的,實屬不易,請尊重原創,謝謝大家!

在上一篇部落格中已經驗證了伺服器有效性:https://blog.csdn.net/qq_41782425/article/details/85321424

一丶概論

  • 公衆号接收與發送消息

驗證URL有效性成功後即接入生效,成為開發者。如果公衆号類型為服務号(訂閱号隻能使用普通消息接口),可以在公衆平台網站中申請認證,認證成功的服務号将獲得衆多接口權限,以滿足開發者需求。

此後使用者每次向公衆号發送消息、或者産生自定義菜單點選事件時,開發者填寫的伺服器配置URL将得到微信伺服器推送過來的消息和事件,然後開發者可以依據自身業務邏輯進行響應,例如回複消息等。

使用者向公衆号發送消息時,公衆号方收到的消息發送者是一個OpenID,是使用使用者微信号加密後的結果,每個使用者對每個公衆号有一個唯一的OpenID。

1.接收普通消息

當普通微信使用者向公衆賬号發消息時,微信伺服器将POST消息的XML資料包到開發者填寫的URL上。

微信伺服器在五秒内收不到響應會斷掉連接配接,并且重新發起請求,總共重試三次。假如伺服器無法保證在五秒内處理并回複,可以直接回複空串,微信伺服器不會對此作任何處理,并且不會發起重試。

各消息類型的推送使用XML資料包結構,如:

<xml>
<ToUserName><![CDATA[gh_866835093fea]]></ToUserName>
<FromUserName><![CDATA[ogdotwSc_MmEEsJs9-ABZ1QL_4r4]]></FromUserName>
<CreateTime>1478317060</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
<MsgId>6349323426230210995</MsgId>
</xml>
           

注意:

<![CDATA

 與 

]]>

 括起來的資料不會被xml解析器解析 

2.普通消息類别

  1. 文本消息
  2. 圖檔消息
  3. 語音消息
  4. 視訊消息
  5. 小視訊消息
  6. 地理位置消息
  7. 連結消息

文本消息

<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName> 
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
 </xml>
           
微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

3. 回複的消息類型

  1. 文本消息
  2. 圖檔消息
  3. 語音消息
  4. 視訊消息
  5. 音樂消息
  6. 圖文消息

回複文本消息

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
           

 注:開發文檔可以到 https://mp.weixin.qq.com/wiki/home/index.html  進行閱讀檢視

二丶代碼實作

需求:我們現在來實作一個針對文本消息的收發程式。實作的業務邏輯,關注者發什麼内容,我們就傳回給什麼内容。

說明:微信伺服器推送消息還是往/wechat/8007,是以在之前代碼上進行修改即可

1.開發步驟

  • step1 如何區分微信伺服器發過來的是第一次的驗證操作還是消息操作
  • 驗證操作為GET請求,消息操作為POST請求
@app.route("/wechat8007", methods=["GET", "POST"])
           
  • step2 參數變更,echostr參數隻是在第一次驗證的時候需要,無論是POST請求還是GET請求這三種參數都必要要,因為需要驗證是不是微信伺服器發送過來的資料
signature = request.args.get("signature")
timestamp = request.args.get("timestamp")
nonce = request.args.get("nonce")
           
  • step3 對微信伺服器發送的請求進行驗證判斷,如果是GET請求,那麼代表是第一次的驗證操作,那麼就需要擷取echostr字段的内容,如果内容為空則抛出404,存在則傳回echostr
if request.method == "GET":
    # 表示是第一次接入微信伺服器的驗證
    echostr = request.args.get("echostr")
    if not echostr:
        abort(404)
    return echostr
           
  • step4  如果為POST請求,那麼代表為微信伺服器轉發消息過來,擷取請求中的data xml資料 ,資料為空抛出400
elif request.method == "POST":
    # 表示微信伺服器轉發消息過來
    xml_str = request.data
    if not xml_str:
        abort(400)
           
  • step5 将對擷取的資料進行解析,通過xmltodict子產品中的parse方法将字元串類型的xml資料,轉換成字典類型的xml格式資料,因為xml資料最外層有一個<xml></xml>标簽,通過get方式擷取标簽裡的内容,再通過get擷取内容中的MsgType消息類型字段的值
# 對xml字元串進行解析
xml_dict = xmltodict.parse(xml_str)
xml_dict = xml_dict.get("xml")

# 提取消息類型
msg_type = xml_dict.get("MsgType")
           
  • step6 對消息類型進行判斷,如果為text文本消息,則傳回文本消息,不是文本消息還是傳回文本消息,這裡可以拓展為(image,voice,video等等可以檢視開發文檔),這裡為了示範,就簡單寫寫
if msg_type == "text":
    # 表示發送的是文本消息
    # 構造傳回值,經由微信伺服器回複給使用者的消息内容
    resp_dict = {
        "xml": {
            "ToUserName": xml_dict.get("FromUserName"),
            "FromUserName": xml_dict.get("ToUserName"),
            "CreateTime": int(time.time()),
            "MsgType": "text",
            "Content": "taogang say:" + xml_dict.get("Content")
        }
    }
else:
    resp_dict = {
        "xml": {
            "ToUserName": xml_dict.get("FromUserName"),
            "FromUserName": xml_dict.get("ToUserName"),
            "CreateTime": int(time.time()),
            "MsgType": "text",
            "Content": "Dear I Love you so much"
        }
    }
           
  • step7 最後将我們構造的響應傳回值通過unparse方法轉換成xml格式的字元串,傳回給微信伺服器
# 将字典轉換為xml字元串
resp_xml_str = xmltodict.unparse(resp_dict)
# 傳回消息資料給微信伺服器
return resp_xml_str
           

2.部署測試

  • step1 将代碼推送到伺服器上
微信公衆号開發之接收與發送消息一丶概論二丶代碼實作
  • step2 在伺服器上進入虛拟環境,運作此程式
微信公衆号開發之接收與發送消息一丶概論二丶代碼實作
  • step3 進入微信公衆平台,用手機掃描測試号二維碼,進行關注測試
微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

掃碼後進行關注 

微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

關注後進入此公衆号,公衆号則發送我們在開發步驟step 6,Dear I Love you so much 消息内容

微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

回到伺服器程式運作日志上,顯示為POST請求,說明程式邏輯完全沒問題

微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

公衆号測試平台使用者清單将我的微信添加上去了

微信公衆号開發之接收與發送消息一丶概論二丶代碼實作
  • step4 測試,在關注的此公衆中,進行消息(文本,表情,語言,圖檔,視訊)發送,當消息類型為文本時,即傳回此消息内容,如果不是都是傳回文本類型,内容為Dear I Love you so much
微信公衆号開發之接收與發送消息一丶概論二丶代碼實作
微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

此時的伺服器代碼運作日志

微信公衆号開發之接收與發送消息一丶概論二丶代碼實作

3.完整代碼

# coding:utf-8
from flask import Flask, request, abort, render_template
import hashlib
import xmltodict, time


# 常量
# 微信的token令牌
WECHAT_TOKEN = "cdtaogang"


app = Flask(__name__)


@app.route("/wechat8007", methods=["GET", "POST"])
def wechat():
    """對接微信公衆号伺服器"""
    # 接收微信伺服器發送的參數
    signature = request.args.get("signature")
    timestamp = request.args.get("timestamp")
    nonce = request.args.get("nonce")

    if not all([signature, timestamp, nonce]):
        abort(400)

    # 按照微信的流程進行計算簽名
    li = [WECHAT_TOKEN, timestamp, nonce]
    # 排序
    li.sort()
    # 拼接字元串
    tmp_str = ''.join(li)
    # 進行sha1加密, 得到正确的簽名值
    sign = hashlib.sha1(tmp_str).hexdigest()
    # 将自己計算的簽名值與請求的簽名參數進行對比,如果相同,則證明請求來自微信伺服器
    if sign != signature:
        # 表示請求不是微信發的
        abort(403)
    else:
        # 表示是微信發送的請求
        if request.method == "GET":
            # 表示是第一次接入微信伺服器的驗證
            echostr = request.args.get("echostr")
            if not echostr:
                abort(404)
            return echostr
        elif request.method == "POST":
            # 表示微信伺服器轉發消息過來
            xml_str = request.data
            if not xml_str:
                abort(400)

            # 對xml字元串進行解析
            xml_dict = xmltodict.parse(xml_str)
            xml_dict = xml_dict.get("xml")

            # 提取消息類型
            msg_type = xml_dict.get("MsgType")

            if msg_type == "text":
                # 表示發送的是文本消息
                # 構造傳回值,經由微信伺服器回複給使用者的消息内容
                resp_dict = {
                    "xml": {
                        "ToUserName": xml_dict.get("FromUserName"),
                        "FromUserName": xml_dict.get("ToUserName"),
                        "CreateTime": int(time.time()),
                        "MsgType": "text",
                        "Content": "taogang say:" + xml_dict.get("Content")
                    }
                }
            else:
                resp_dict = {
                    "xml": {
                        "ToUserName": xml_dict.get("FromUserName"),
                        "FromUserName": xml_dict.get("ToUserName"),
                        "CreateTime": int(time.time()),
                        "MsgType": "text",
                        "Content": "Dear I Love you so much"
                    }
                }
            # 将字典轉換為xml字元串
            resp_xml_str = xmltodict.unparse(resp_dict)
            # 傳回消息資料給微信伺服器
            return resp_xml_str

if __name__ == '__main__':
    app.run(port=8007, debug=True)
           

繼續閱讀

最近更新