使用 Go 语言的 Web 框架 Gin 进行微信公众号接入,并实现对微信消息的接收以及回复处理。html
同时借助 nginx 代理服务器对代理的端口号以及 URI 进行优化处理。nginx
在文章末尾给出该 Demo 的项目地址。git
这里使用微信公众平台提供的接口测试号用于开发使用,接口测试号申请。github
公众号的接入主要有两个步骤,微信公众平台接入指南:服务器
第一步须要配置服务器的 URL 地址,而且必须以 http://
或 https://
开头,分别支持 80 端口和 443 端口;还需配置一个 3 ~ 32 位字符的 Token,用于消息验证。微信
第二步用于验证消息来源的正确性,当第一步配置完成并点提交后,微信服务器将发送 GET 请求到填写的服务器地址上,GET 请求携带的参数及描述以下表所示:微信公众平台
参数 | 描述 |
---|---|
signature | 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
服务器须要作的验证操做流程大体为:框架
token
、timestamp
、nonce
三个参数进行字典序排序;token
、timestamp
、nonce
三个参数按顺序拼接成一个字符串,并对该字符串进行 sha1
加密;signature
参数进行比较,若是字符串值相同,则表示校验经过,将 echostr
参数原样返回便可。使用 Go 实现的微信公众号接入代码以下:函数
package main
import (
"github.com/gin-gonic/gin"
"log"
"weixin-demo/util"
)
// 与填写的服务器配置中的Token一致
const Token = "coleliedev"
func main() {
router := gin.Default()
router.GET("/wx", WXCheckSignature)
log.Fatalln(router.Run(":80"))
}
// WXCheckSignature 微信接入校验
func WXCheckSignature(c *gin.Context) {
signature := c.Query("signature")
timestamp := c.Query("timestamp")
nonce := c.Query("nonce")
echostr := c.Query("echostr")
ok := util.CheckSignature(signature, timestamp, nonce, Token)
if !ok {
log.Println("微信公众号接入校验失败!")
return
}
log.Println("微信公众号接入校验成功!")
_, _ = c.Writer.WriteString(echostr)
}
复制代码
package util
import (
"crypto/sha1"
"encoding/hex"
"sort"
"strings"
)
// CheckSignature 微信公众号签名检查
func CheckSignature(signature, timestamp, nonce, token string) bool {
arr := []string{timestamp, nonce, token}
// 字典序排序
sort.Strings(arr)
n := len(timestamp) + len(nonce) + len(token)
var b strings.Builder
b.Grow(n)
for i := 0; i < len(arr); i++ {
b.WriteString(arr[i])
}
return Sha1(b.String()) == signature
}
// 进行Sha1编码
func Sha1(str string) string {
h := sha1.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
复制代码
最后,将项目部署至服务器,并在接口配置信息中点击提交按钮,完成微信公众号的接入。测试
需注意,因为该 Web 程序需监听 80 端口,因此服务器不能有其余监听 80 端口的程序,如 nginx
。
完成微信公众号的接入后,接下来以普通消息接收和被动回复用户消息这两个 API 为例,来完成 Go 对微信消息的接收和回复处理的具体实现。
首先是消息接收,参考微信官方文档,接收普通消息。
当普通微信用户向公众帐号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
以文本消息为例,其 XML
数据包结构以及参数描述分别以下:
<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>
复制代码
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方账号(一个OpenID) |
CreateTime | 消息建立时间 (整型) |
MsgType | 消息类型,文本为text |
Content | 文本消息内容 |
MsgId | 消息id,64位整型 |
明白了微信服务器向开发服务器传递微信用户消息的方式以及传递的数据包结构后,可知进行消息接收开发,大体须要进行两个步骤:
POST
类型请求的处理函数;XML
数据包进行解析。对于第二步,咱们能够借助 Gin 框架的 ShouldBindXML
或 BindXML
方法来对 XML
数据包进行解析。
使用 Go 实现的消息接收代码以下:
package main
import (
"github.com/gin-gonic/gin"
"log"
"weixin-demo/util"
)
const Token = "coleliedev"
func main() {
router := gin.Default()
router.GET("/wx", WXCheckSignature)
router.POST("/wx", WXMsgReceive)
log.Fatalln(router.Run(":80"))
}
// WXTextMsg 微信文本消息结构体
type WXTextMsg struct {
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Content string
MsgId int64
}
// WXMsgReceive 微信消息接收
func WXMsgReceive(c *gin.Context) {
var textMsg WXTextMsg
err := c.ShouldBindXML(&textMsg)
if err != nil {
log.Printf("[消息接收] - XML数据包解析失败: %v\n", err)
return
}
log.Printf("[消息接收] - 收到消息, 消息类型为: %s, 消息内容为: %s\n", textMsg.MsgType, textMsg.Content)
}
复制代码
将添加消息接收的代码更新至服务器后,对该接口测试号发送消息,可在服务器查看到以下记录:
接下来以被动回复用户消息这个 API 为例,实现对微信用户发送的消息的回复,参考微信官方文档,被动消息回复。
消息回复与消息接收相似,都须要使用 XML
格式的数据包,回复文本消息须要的 XML
数据包结构以及参数以下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
复制代码
参数 | 是否必须 | 描述 |
---|---|---|
ToUserName | 是 | 接收方账号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息建立时间 (整型) |
MsgType | 是 | 消息类型,文本为text |
Content | 是 | 回复的消息内容(换行:在content中可以换行,微信客户端就支持换行显示) |
使用 Go 实现的消息回复代码以下:
package main
import (
"encoding/xml"
"fmt"
"github.com/gin-gonic/gin"
"log"
"time"
"weixin-demo/util"
)
const Token = "coleliedev"
func main() {
router := gin.Default()
router.GET("/wx", WXCheckSignature)
router.POST("/wx", WXMsgReceive)
log.Fatalln(router.Run(":80"))
}
// WXMsgReceive 微信消息接收
func WXMsgReceive(c *gin.Context) {
var textMsg WXTextMsg
err := c.ShouldBindXML(&textMsg)
if err != nil {
log.Printf("[消息接收] - XML数据包解析失败: %v\n", err)
return
}
log.Printf("[消息接收] - 收到消息, 消息类型为: %s, 消息内容为: %s\n", textMsg.MsgType, textMsg.Content)
// 对接收的消息进行被动回复
WXMsgReply(c, textMsg.ToUserName, textMsg.FromUserName)
}
// WXRepTextMsg 微信回复文本消息结构体
type WXRepTextMsg struct {
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Content string
// 若不标记XMLName, 则解析后的xml名为该结构体的名称
XMLName xml.Name `xml:"xml"`
}
// WXMsgReply 微信消息回复
func WXMsgReply(c *gin.Context, fromUser, toUser string) {
repTextMsg := WXRepTextMsg{
ToUserName: toUser,
FromUserName: fromUser,
CreateTime: time.Now().Unix(),
MsgType: "text",
Content: fmt.Sprintf("[消息回复] - %s", time.Now().Format("2006-01-02 15:04:05")),
}
msg, err := xml.Marshal(&repTextMsg)
if err != nil {
log.Printf("[消息回复] - 将对象进行XML编码出错: %v\n", err)
return
}
_, _ = c.Writer.Write(msg)
}
复制代码
须要注意的是 WXRepTextMsg
结构体中必须添加 XMLName
属性,而且对该属性进行 xml
标记,用于将该 xml
名标记为 xml
,即便用 xml.Marshal
方法对该结构体对象进行编码后,获得的 xml
数据的最外层标签为 <xml></xml>
,如若不添加该 XMLName
属性,则编码后获得的 xml
数据的最外层标签为 <WXRepTextMsg></WXRepTextMsg>
,不符合微信官方要求的 xml
数据包格式,所以全部 xml
名称即编码后的 xml
数据的最外层标签不为 <xml></xml>
的数据包都没法成功回复。
将添加消息回复的代码更新至服务器后,向服务器发送消息将收到以下回复:
一般服务器都不会把 80 端口或 443 端口交给 Web 程序,这时可以使用 nginx
做为代理服务器,将 Web 程序跑在其它端口上,如 8002,让 nginx
监听 80 端口或 443 端口,并对指定的 URI 进行反向代理操做,如如下配置,将把 80 端口 URI 为 /weixin
的请求代理到服务器本地的 8002 端口的 /wx
上:
server {
listen 80;
location /weixin {
proxy_pass http://127.0.0.1:8002/wx;
proxy_redirect default;
}
}
复制代码
修改程序监听的端口号为 8002:
func main() {
router := gin.Default()
router.GET("/wx", WXCheckSignature)
router.POST("/wx", WXMsgReceive)
log.Fatalln(router.Run(":8002"))
}
复制代码
修改微信公众号接入接口配置:
最后测试结果以下:
从 nginx
的日志文件中能够看到其确实收到了 URI 为 /weixin
的请求,而且在该 Web 程序的日志文件中,也能够看到其收到的 URI 为 /wx
的请求,经过观察两份日志记录的请求参数,能够发现,nginx
作的代理是成功的。
最后作一个对该文章的小结,这篇文章主要使用了 Go 语言的 Gin 框架以及借助微信接口测试号,完成了对微信公众号接入的开发,以及实现接收微信用户消息和回复微信用户消息的两个功能。
以及,感谢你们的耐心阅读!!!
演示 Demo github 地址:github.com/hkail/weixi…
演示 Demo gitee 地址:gitee.com/hkail/weixi…