1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-23 13:42:25 +08:00
Files
wechat/miniprogram/server/server.go
stuchilde c5c764dcc9 feat(miniapp): 小程序订阅消息
1. 用户订阅消息服务端回调处理
2. 用户订阅消息订阅通知事件推送
2021-08-01 19:57:54 +08:00

200 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package server
import (
"encoding/xml"
"errors"
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/miniprogram/message"
"github.com/silenceper/wechat/v2/util"
"io/ioutil"
"net/http"
"reflect"
"runtime/debug"
"strconv"
)
// Server struct
type Server struct {
*context.Context
Write http.ResponseWriter
Request *http.Request
skipValidate bool
openID string
messageHandler func(mixMessage *message.MiniProgramMixMessage) *message.Reply
RequestRawXMLMsg []byte
RequestMsg *message.MiniProgramMixMessage
ResponseRawXMLMsg []byte
ResponseMsg interface{}
isSafeMode bool
random []byte
nonce string
timestamp int64
}
func NewServer(context *context.Context) *Server {
srv := new(Server)
srv.Context = context
return srv
}
func (srv *Server) Server() error {
if !srv.Validate() {
return fmt.Errorf("请求签名校验失败")
}
echoStr := srv.Query("echostr")
if echoStr != "" {
srv.SetResponseWrite(echoStr)
return nil
}
response, err := srv.handleRequest()
if err != nil {
return err
}
return srv.buildResponse(response)
}
// SkipValidate 设置跳过签名校验
func (srv *Server) SkipValidate(skip bool) {
srv.skipValidate = skip
}
// Validate 校验请求是否合法
func (srv *Server) Validate() bool {
if srv.skipValidate {
return true
}
timestamp := srv.Query("timestamp")
nonce := srv.Query("nonce")
signature := srv.Query("signature")
return signature == util.Signature(srv.Token, timestamp, nonce)
}
func (srv *Server) handleRequest() (reply *message.Reply, err error) {
//set isSafeMode
srv.isSafeMode = false
encryptType := srv.Query("encrypt_type")
if encryptType == "aes" {
srv.isSafeMode = true
}
//set openID
srv.openID = srv.Query("openid")
var msg interface{}
msg, err = srv.getMessage()
if err != nil {
return
}
mixMessage, success := msg.(*message.MiniProgramMixMessage)
if !success {
err = errors.New("消息类型转换失败")
}
srv.RequestMsg = mixMessage
reply = srv.messageHandler(mixMessage)
return
}
//GetOpenID return openID
func (srv *Server) GetOpenID() string {
return srv.openID
}
func (srv *Server) getMessage() (interface{}, error) {
var rawXMLMsgBytes []byte
var err error
if srv.isSafeMode {
var encryptedXMLMsg message.EncryptedXMLMsg
if err := xml.NewDecoder(srv.Request.Body).Decode(&encryptedXMLMsg); err != nil {
return nil, fmt.Errorf("从body中解析xml失败err=%v", err)
}
//验证消息签名
timestamp := srv.Query("timestamp")
srv.timestamp, err = strconv.ParseInt(timestamp, 10, 32)
if err != nil {
return nil, err
}
nonce := srv.Query("nonce")
srv.nonce = nonce
msgSignature := srv.Query("msg_signature")
msgSignatureGen := util.Signature(srv.Token, timestamp, nonce, encryptedXMLMsg.EncryptedMsg)
if msgSignature != msgSignatureGen {
return nil, fmt.Errorf("消息不合法,验证签名失败")
}
// 解密
srv.random, rawXMLMsgBytes, err = util.DecryptMsg(srv.AppID, encryptedXMLMsg.EncryptedMsg, srv.EncodingAESKey)
if err != nil {
return nil, fmt.Errorf("消息解密失败, err=%v", err)
}
} else {
rawXMLMsgBytes, err = ioutil.ReadAll(srv.Request.Body)
if err != nil {
return nil, fmt.Errorf("从body中解析xml失败, err=%v", err)
}
}
srv.RequestRawXMLMsg = rawXMLMsgBytes
return srv.parseRequestMessage(rawXMLMsgBytes)
}
func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg *message.MiniProgramMixMessage, err error) {
msg = &message.MiniProgramMixMessage{}
err = xml.Unmarshal(rawXMLMsgBytes, msg)
return
}
func (srv *Server) SetMessageHandler(handler func(*message.MiniProgramMixMessage) *message.Reply) {
srv.messageHandler = handler
}
func (srv *Server) buildResponse(reply *message.Reply) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("panic error: %v\n%s", e, debug.Stack())
}
}()
if reply == nil {
return nil
}
msgType := reply.MsgType
switch msgType {
case message.MsgTypeEvent:
case message.MsgTypeImage:
case message.MsgTypeLink:
case message.MsgTypeText:
case message.MsgTypeMiniProgramPage:
default:
err = message.ErrUnsupportedReply
return
}
msgData := reply.MsgData
value := reflect.ValueOf(msgData)
//msgData must be a ptr
kind := value.Kind().String()
if kind != "ptr" {
return message.ErrUnsupportedReply
}
params := make([]reflect.Value, 1)
params[0] = reflect.ValueOf(srv.RequestMsg.FromUserName)
value.MethodByName("SetToUserName").Call(params)
params[0] = reflect.ValueOf(srv.RequestMsg.ToUserName)
value.MethodByName("SetFromUserName").Call(params)
params[0] = reflect.ValueOf(srv.RequestMsg.MsgType)
value.MethodByName("SetMsgType").Call(params)
params[0] = reflect.ValueOf(util.GetCurrTS())
value.MethodByName("SetCreateTime").Call(params)
srv.ResponseMsg = msgData
srv.ResponseRawXMLMsg, err = xml.Marshal(msgData)
return
}