Gin, the Web framework of Go language, is used to access wechat public accounts, and to receive and reply wechat messages.

Meanwhile, nginx proxy server is used to optimize the port number and URI of proxy.

The project address for the Demo is given at the end of the article.

directory

  • Public Account access
  • The message received
  • Reply message
  • Use the Ngxin proxy server
  • summary

Public Account access

The interface test number provided by wechat public platform is used for development and application.

There are two main steps to access the public account. Access guide for wechat public platform:

  1. Filling in the Server Configuration
  2. Verify the validity of the server address

The first step is to set the URL of the server. The URL must start with http:// or https://, which supports port 80 and port 443 respectively. You also need to configure a Token of 3 to 32 characters for message authentication.

The second step is used to verify the correctness of the message source. After the configuration in the first step is completed and submission is clicked, the wechat server will send a GET request to the server address filled in. The parameters and description of the GET request are shown in the following table:

parameter describe
signature Wechat encryption signature and signature combine token parameters filled in by the developer with timestamp parameters and nonce parameters in the request.
timestamp The time stamp
nonce The random number
echostr Random string

The server verification process is as follows:

  1. righttoken,timestamp,nonceLexicographical sorting of three parameters;
  2. I’m going to sort thattoken,timestamp,nonceThe three arguments are sequentially concatenated into a string and are performed on the stringsha1Encryption;
  3. Use the encrypted string andsignatureIf the string values are the same, the verification passesechostrThe parameter is returned as is.

The access code of wechat public account realized by Go is as follows:

package main

import (
	"github.com/gin-gonic/gin"
	"log"
	"weixin-demo/util"
)

// The Token is the same as that specified in the server configuration
const Token = "coleliedev"

func main(a) {
	router := gin.Default()

	router.GET("/wx", WXCheckSignature)

	log.Fatalln(router.Run(": 80"))}// WXCheckSignature verify wechat access
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("Wechat public account access verification failed!")
		return
	}

	log.Println("Wechat public account access verification success!")
	_, _ = c.Writer.WriteString(echostr)
}
Copy the code
package util

import (
	"crypto/sha1"
	"encoding/hex"
	"sort"
	"strings"
)

// CheckSignature check wechat public id signature
func CheckSignature(signature, timestamp, nonce, token string) bool {
	arr := []string{timestamp, nonce, token}
	// lexicographical sort
	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
}

// Encode Sha1
func Sha1(str string) string {
	h := sha1.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))}Copy the code

Finally, deploy the project to the server and click the submit button in the interface configuration information to complete the access of the wechat public account.

Note that since this Web application listens on port 80, the server cannot have other programs that listen on port 80, such as nginx.

The message received

After the wechat public account is connected, the two apis of ordinary message reception and passive reply to user messages are taken as examples to complete the specific implementation of Go’s reception and reply processing of wechat messages.

The first is message reception. Refer to official wechat documents to receive ordinary messages.

When ordinary wechat users send messages to public accounts, the wechat server sends the XML packet of the POST message to the URL filled in by the developer.

Take text message as an example, its XML packet structure and parameter description are as follows:

<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>
Copy the code
parameter describe
ToUserName Developer wechat account
FromUserName Sender account (one OpenID)
CreateTime Message creation time (integer)
MsgType The message type is text
Content Text message content
MsgId The message ID is a 64-bit integer

After understanding the way in which wechat server transmits wechat user messages to the development server and the packet structure transmitted, we know that there are roughly two steps for message reception and development:

  1. Create handles the wechat server sent to the development serverPOSTHandlers for type requests;
  2. On the requestXMLThe packet is parsed.

For the second step, we can use the Gin framework’s ShouldBindXML or BindXML method to parse the XML packets.

The message receiving code implemented with Go is as follows:

package main

import (
	"github.com/gin-gonic/gin"
	"log"
	"weixin-demo/util"
)

const Token = "coleliedev"

func main(a) {
	router := gin.Default()

	router.GET("/wx", WXCheckSignature)
	router.POST("/wx", WXMsgReceive)

	log.Fatalln(router.Run(": 80"))}// WXTextMsg wechat text message structure
type WXTextMsg struct {
	ToUserName   string
	FromUserName string
	CreateTime   int64
	MsgType      string
	Content      string
	MsgId        int64
}

// WXMsgReceive receives wechat messages
func WXMsgReceive(c *gin.Context) {
	var textMsg WXTextMsg
	err := c.ShouldBindXML(&textMsg)
	iferr ! =nil {
		log.Printf("[message receive] -XML packet parsing failure: %v\n", err)
		return
	}

	log.Printf("[Message receive] - Received message with type %s and content %s\n", textMsg.MsgType, textMsg.Content)
}
Copy the code

After updating the code for adding messages to receive to the server, a message is sent to the interface test number. The following records can be viewed on the server:

Reply message

Next, the API of passive reply to user messages is taken as an example to realize the reply to messages sent by wechat users. Refer to wechat official documents for passive reply.

Message reply and message receiving are similar in that both require data packets in XML format. The structure and parameters of XML data packets required for text message reply are as follows:

<xml>
  <ToUserName><! [CDATA[toUser]]></ToUserName>
  <FromUserName><! [CDATA[fromUser]]></FromUserName>
  <CreateTime>12345678</CreateTime>
  <MsgType><! [CDATA[text]]></MsgType>
  <Content><! [CDATA [you]] ></Content>
</xml>
Copy the code
parameter Whether must describe
ToUserName is Recipient account (OpenID received)
FromUserName is Developer wechat account
CreateTime is Message creation time (integer)
MsgType is The message type is text
Content is Message content of the reply (line break: line break can be used in content, wechat client supports line break display)

The message reply code implemented with Go is as follows:

package main

import (
	"encoding/xml"
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"time"
	"weixin-demo/util"
)

const Token = "coleliedev"

func main(a) {
	router := gin.Default()

	router.GET("/wx", WXCheckSignature)
	router.POST("/wx", WXMsgReceive)

	log.Fatalln(router.Run(": 80"))}// WXMsgReceive receives wechat messages
func WXMsgReceive(c *gin.Context) {
	var textMsg WXTextMsg
	err := c.ShouldBindXML(&textMsg)
	iferr ! =nil {
		log.Printf("[message receive] -XML packet parsing failure: %v\n", err)
		return
	}

	log.Printf("[Message receive] - Received message with type %s and content %s\n", textMsg.MsgType, textMsg.Content)

	// Passively responds to received messages
	WXMsgReply(c, textMsg.ToUserName, textMsg.FromUserName)
}

// WXRepTextMsg wechat reply text message structure
type WXRepTextMsg struct {
	ToUserName   string
	FromUserName string
	CreateTime   int64
	MsgType      string
	Content      string
	// If XMLName is not marked, the parsed XML is the name of the structure
	XMLName      xml.Name `xml:"xml"`
}

// WXMsgReply replies to wechat messages
func WXMsgReply(c *gin.Context, fromUser, toUser string) {
	repTextMsg := WXRepTextMsg{
		ToUserName:   toUser,
		FromUserName: fromUser,
		CreateTime:   time.Now().Unix(),
		MsgType:      "text",
		Content:      fmt.Sprintf("[message reply] - %s", time.Now().Format("The 2006-01-02 15:04:05")),
	}

	msg, err := xml.Marshal(&repTextMsg)
	iferr ! =nil {
		log.Printf("[message reply] - Error encoding object XML: %v\n", err)
		return
	}
	_, _ = c.Writer.Write(msg)
}
Copy the code

Note that the XMLName attribute must be added to the WXRepTextMsg structure and the XML tag must be applied to mark the XML name as XML. That is, after encoding the structure object using the xml.Marshal method, The outermost tag of the resulting XML data is < XML >
. If the XMLName attribute is not added, the outermost tag of the encoded XML data is
. The format of XML data package does not meet the official requirements of wechat. Therefore, all data packets whose XML name, namely the outermost tag of encoded XML data, is not < XML >
cannot be successfully replied.

After updating the code to add message reply to the server, sending a message to the server will receive the following reply:

Use the Nginx proxy server

Usually the server will not give port 80 or 443 to the Web program, then you can use nginx as a proxy server, run the Web program on other ports, such as 8002, let Nginx listen to port 80 or 443, and the specified URI reverse proxy operation. If the following configuration is used, the request on port 80 URI /weixin will be proxied to /wx on port 8002 locally on the server:

server {
	listen 80;

	location /weixin {
		proxy_pass http://127.0.0.1:8002/wx;
		proxy_redirectdefault; }}Copy the code

Change the program listening port number to 8002:

func main(a) {
	router := gin.Default()

	router.GET("/wx", WXCheckSignature)
	router.POST("/wx", WXMsgReceive)

	log.Fatalln(router.Run(": 8002"))}Copy the code

Modify the configuration of wechat public account access interface:

The final test results are as follows:

From the log file of nginx, it can be seen that it did receive a request with the URI /weixin, and in the log file of the Web program, it can also be seen that it received a request with the URI /wx. By observing the request parameters recorded in the two logs, it can be found that, Nginx is a successful proxy.

summary

Finally, a summary of this article is made. This article mainly uses the Gin framework of Go language and the wechat interface test number to complete the development of wechat public account access, and realize the two functions of receiving and replying wechat user messages.

And thank you for your patience!!

Github address: github.com/hkail/weixi…

Gitee address: gitee.com/hkail/weixi…