内容简介:使用 Go 语言的 Web 框架 Gin 进行微信公众号接入,并实现对微信消息的接收以及回复处理。同时借助 nginx 代理服务器对代理的端口号以及 URI 进行优化处理。在文章末尾给出该 Demo 的项目地址。
使用 Go 语言的 Web 框架 Gin 进行微信公众号接入,并实现对微信消息的接收以及回复处理。
同时借助 nginx 代理服务器对代理的端口号以及 URI 进行优化处理。
在文章末尾给出该 Demo 的项目地址。
目录
- 公众号接入
- 消息接收
- 消息回复
- 使用 ngxin 代理服务器
- 小结
公众号接入
这里使用微信公众平台提供的接口测试号用于开发使用, 接口测试号申请 。
公众号的接入主要有两个步骤, 微信公众平台接入指南 :
- 填写服务器配置
- 验证服务器地址的有效性
第一步需要配置 服务器的 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>
的数据包都无法成功回复。
将添加消息回复的代码更新至服务器后,向服务器发送消息将收到如下回复:
使用 nginx 代理服务器
通常服务器都不会把 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
做的代理是成功的。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 云转码接入视频网站解决方案 express-ffmpeg接入discuz方案
- 数据接入治理平台
- 【Netty】如何接入新连接
- 有赞统一接入层架构演进
- Bytom矿池接入协议指南
- 谈谈业务容器化 ———— 降低接入成本
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构 Python语言描述
[美] Kenneth A. Lambert 兰伯特 / 李军 / 人民邮电出版社 / 2017-12-1 / CNY 69.00
在计算机科学中,数据结构是一门进阶性课程,概念抽象,难度较大。Python语言的语法简单,交互性强。用Python来讲解数据结构等主题,比C语言等实现起来更为容易,更为清晰。 《数据结构 Python语言描述》第1章简单介绍了Python语言的基础知识和特性。第2章到第4章对抽象数据类型、数据结构、复杂度分析、数组和线性链表结构进行了详细介绍,第5章和第6章重点介绍了面向对象设计的相关知识、......一起来看看 《数据结构 Python语言描述》 这本书的介绍吧!