go语言发送微信小程序模板消息

PS:开发微信的东西是真的心累,一大堆坑!文档写的乱七八糟的,找个东西都得半天。

为了发一条模板消息翻了无数个博客,很多都是把代码一放,其实代码这块很好弄,不就组装个数据调一下API吗,主要是前期工作。我把我遇到的坑给大家总结一下,希望后来人可以少走一些弯路。

微信发送模板消息文档:点击查看

  1. 超级大坑!发送接口问题

文档上是这个接口:

https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN

实际不是的!!!!!是下面这个,看见没,多了一个wxopen,如果你碰到 48001 返回码,看看是不是这出问题了。

https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=ACCESS_TOKEN
  1. 域名问题

他们这个必须要配置合法域名,必须是https的,要有SSL证书的,在微信开发工具上可以看到你目前配置的合法域名

  1. 内网穿透问题

因为formID必须要在真机上才可以获取,所以你最好设置一个内网穿透,让手机能访问到你本地的服务。

内网穿透工具可以去下面网站上下载,有免费的
https://www.ngrok.cc

  1. 证书问题
    在腾讯云上下载的证书是下面这个样子的:

这时你在go语言就不知道用哪个里面的证书了。。。我用的gofram框架,可以这样搞,
将Apache里面的

这两个东西,通过下面网站生成一个 .pem证书然后再用到go语言中就好使了。
https://www.myssl.cn/tools/merge-pem-cert.html
go语言中这样用

s.EnableHTTPS("https/ssl.pem", "https/3_pibigstar.com.key")
s.SetHTTPSPort(7777)

还有一个坑就是,端口不能是443,可能是我本机是Windows,把443端口屏蔽了,如果你一直出现404情况,换个端口!

大概就些坑,如果你碰到其他的坑可以给我留言,或关注我的微信公众号,希望可以帮到你。

放代码

go后端

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/pibigstar/go-todo/config"
	"github.com/pibigstar/go-todo/constant"
	"github.com/pibigstar/go-todo/models/db"
)

// 发送模板消息

var (
	send_template_url    = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=%s"
	get_access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
)

// SendTemplate 发送模板消息
func SendTemplate(msg *TemplateMsg) (*SendTemplateResponse, error) {
	msg.Miniprogram.AppID = config.ServerConfig.Appid
	accessToken, err := getAccessToken(msg.Touser)
	if err != nil {
		log.Error("获取accessToken失败")
		return nil, err
	}
	url := fmt.Sprintf(send_template_url, accessToken.AccessToken)
	data, err := json.Marshal(msg)
	if err != nil {
		log.Error("模板消息JSON格式错误", "err", err.Error())
		return nil, err
	}
	client := http.Client{}
	resp, err := client.Post(url, "application/json", bytes.NewBuffer(data))
	if err != nil {
		log.Error("网络错误,发送模板消息失败", "err", err.Error())
		return nil, err
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	var templateResponse SendTemplateResponse
	err = json.Unmarshal(body, &templateResponse)
	if err != nil {
		log.Error("解析responseBody错误", "err", err.Error())
		return nil, err
	}
	return &templateResponse, nil
}

func getAccessToken(openID string) (*GetAccessTokenResponse, error) {
	var accessTokenResponse GetAccessTokenResponse
	// 先从redis中拿
	accessToken, err := getAccessTokenFromRedis(openID)
	if accessToken != "" && err == nil {
		accessTokenResponse = GetAccessTokenResponse{AccessToken: accessToken}
		log.Info("从redis中获取到access_token", "access_token", accessToken)
		return &accessTokenResponse, nil
	}
	appID := config.ServerConfig.Appid
	secret := config.ServerConfig.Secret
	url := fmt.Sprintf(get_access_token_url, appID, secret)
	client := http.Client{}
	resp, err := client.Get(url)
	if err != nil {
		log.Error("获取access_toke网络异常", "err", err.Error())
		return nil, err
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	err = json.Unmarshal(body, &accessTokenResponse)
	if err != nil {
		log.Error("解析AccessToken失败", "err", err.Error())
		return nil, err
	}
	// 存到redis中
	if _, err := setAccessTokenToRedis(openID, accessTokenResponse.AccessToken); err != nil {
		log.Error("将access_token存储到redis中失败", "err", err.Error())
	}
	return &accessTokenResponse, nil
}

// 从redis中取access_token
func getAccessTokenFromRedis(openID string) (string, error) {
	key := fmt.Sprintf(constant.Access_Token_Redis_Prefix, openID)
	accessToken, err := db.Redis.Get(key).Result()
	return accessToken, err
}

// 将access_token存储到redis中
func setAccessTokenToRedis(openID, token string) (string, error) {
	key := fmt.Sprintf(constant.Access_Token_Redis_Prefix, openID)
	b, err := db.Redis.Set(key, token, 7200*time.Second).Result()
	return b, err
}

type TemplateMsg struct {
	Touser      string        `json:"touser"`      //接收者的OpenID
	TemplateID  string        `json:"template_id"` //模板消息ID
	FormID      string        `json:"form_id"`
	URL         string        `json:"url"`         //点击后跳转链接
	Miniprogram Miniprogram   `json:"miniprogram"` //点击跳转小程序
	Data        *TemplateData `json:"data"`
}
type Miniprogram struct {
	AppID    string `json:"appid"`
	Pagepath string `json:"pagepath"`
}

type TemplateData struct {
	First    KeyWordData `json:"first,omitempty"`
	Keyword1 KeyWordData `json:"keyword1,omitempty"`
	Keyword2 KeyWordData `json:"keyword2,omitempty"`
	Keyword3 KeyWordData `json:"keyword3,omitempty"`
	Keyword4 KeyWordData `json:"keyword4,omitempty"`
	Keyword5 KeyWordData `json:"keyword5,omitempty"`
}

type KeyWordData struct {
	Value string `json:"value"`
	Color string `json:"color,omitempty"`
}

type SendTemplateResponse struct {
	Errcode int    `json:"errcode"`
	Errmsg  string `json:"errmsg"`
	MsgID   string `json:"msgid"`
}

type GetAccessTokenResponse struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
}

controller例子

仅仅是个例子,供大家参考

func init() {
	s := g.Server()
	s.BindHandler("/send", sendTemplate)
}

func sendTemplate(r *ghttp.Request) {
	templateMsg := &utils.TemplateMsg{}
	tempData := &utils.TemplateData{}
	tempData.First.Value = "测试模板消息"
	tempData.Keyword1.Value = "大家记得买票啊"
	tempData.Keyword2.Value = "马上就要放假了,大家记得买回家的票啊"
	tempData.Keyword3.Value = "2018-12-30 15:59"
	tempData.Keyword4.Value = "派大星"
	tempData.Keyword5.Value = "记得按时完成"
	templateMsg.Data = tempData
	formID := r.GetString("formID")
	log.Info("formID", "formID", formID)
	templateMsg.FormID = formID
	openID, _ := middleware.GetOpenID(r)
	templateMsg.Touser = openID
	templateMsg.TemplateID = constant.Tmeplate_Receive_Task_ID
	response, err := utils.SendTemplate(templateMsg)
	if err != nil {
		fmt.Println("发送模板消息失败", err.Error())
	}
	r.Response.WriteJson(response)
}

小程序端

<view>
 <form bindsubmit="templateSend" report-submit="true">
      <button type='primary' formType="submit" size='mini'>发送模板消息</button>
    </form>
</view>
  templateSend: function (e) {
    // 表单需设置report-submit="true"
    var formId = e.detail.formId;
    // 发送随机模板消息
    util.apiRequest("send","get",{
      formID: formId,
      }).then(data => {
         console.log(data)
      })
  }
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页