C#服务号推送微信公众号模板消息

news2024/11/14 14:04:23

一、准备工作

微信公众平台:https://mp.weixin.qq.com/

申请测试账号:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

微信推送消息模板不需要发布服务器,也不需要填写授权回调域名,只需要两个微信后台的参数

在公众号后台设置与开发——>基本配置中获取AppID、AppSecret

二、实现思路

我的需求是给特定分组的用户推送模板消息,分为以下4步

  1. 获取access_token(把token存缓存,不用每次请求)

  1. 获取公众号的所有标签,得到目标分组

  1. 获取指定标签下的粉丝列表,得到用户的openid

  1. 给粉丝推送模板消息

三、后端代码

1.获取AccessToken

/// <summary>
/// Access_token 的摘要说明
/// </summary>
public class Access_token
{
    public string access_token{ get;set; }
    public int expires_in { get; set; } //凭证有效时间
    public string errcode { get; set; } //返回码
    public string errmsg { get; set; } //返回说明
    public DateTime CreateTime { get; set; }
}

//token缓存键值对
private static Dictionary<string, Access_token> tokenCache = new Dictionary<string, Access_token>();
//获取access_toke
    /// <summary>
    /// 获取缓存令牌
    /// </summary>
    public static string GetAccessToken(string appid, string secret)
    {
        //token缓存
        Access_token result = null;
        //判断缓存是否存在键:appid,就将缓存中的token赋给result
        if (tokenCache.ContainsKey(appid))
        {
            result = tokenCache[appid];
        }
        //不存在则获取token
        if (result == null)
        {
            result = GetToken(appid, secret);
            result.CreateTime = DateTime.Now;
            tokenCache.Add(appid, result);
        }
        //判断是否在有效期内,过期重新获取token    给10s延迟时间
        else if (System.DateTime.Compare(result.CreateTime.AddSeconds(result.expires_in), System.DateTime.Now) < 7200)
        {
            result = GetToken(appid, secret);
            result.CreateTime = DateTime.Now;
            tokenCache[appid] = result;
        }
        return result.access_token;
    }
  /// <summary>
    /// 获取AccessToken
    /// </summary>
    /// <returns></returns>
    public static Access_token GetToken(string appid, string secret)
    {
        string grant_type = "client_credential";
        string tokenUrl = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", grant_type, appid, secret);
        var wc = new WebClient();
        var strReturn = wc.DownloadString(tokenUrl);
        //得到token
        Access_token tokeninfo = JsonHelper.ToJson<Access_token>(strReturn);
        return tokeninfo;
    }

2.得到所有标签

 static JavaScriptSerializer jszer = new JavaScriptSerializer();

    /// <summary>
    /// 得到所有标签
    /// </summary>
    /// <param name="accesstoken"></param>
    /// <returns></returns>
    public static int GetTagList(string accesstoken)
    {
        int pagecode=0;
        //得到所有标签
        string tagUrl = string.Format("https://api.weixin.qq.com/cgi-bin/tags/get?access_token={0}", accesstoken);
        var wc = new WebClient();

        wc.Encoding = System.Text.Encoding.UTF8;
        var strReturn = wc.DownloadString(tagUrl);
        Root list = jszer.Deserialize<Root>(strReturn);
        //循环遍历所有标签,如果是移动时则赋值pagecode,循环停止
        if (list != null)
        {
            foreach (var item in list.tags)
            {
                if (item.name == "移动")
                {
                    pagecode = item.id;
                    break;
                }
            }
            return pagecode;
        }
        else
        {
            return pagecode;
        }
    }
public class Tags    
{
    /// <summary>
    /// 
    /// </summary>
    public int id { get; set; }
    /// <summary>
    /// 星标组
    /// </summary>
    public string name { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public int count { get; set; }
}

public class Root
{
    /// <summary>
    /// 
    /// </summary>
    public List<Tags> tags { get; set; }
}

3.获取标签下粉丝列表,得到用户的openid

   /// <summary>
    /// 获取标签下粉丝列表
    /// </summary>
    /// <returns></returns>
    public static List<string> GetTagUserList(string token)
    {
        List<string> list = new List<string>();
        int tagecode = GetTagList(token);   
        string tokenUrl = string.Format("https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token={0}", token);
        PostTage strpost = new PostTage();
        strpost.tagid = tagecode;
        strpost.next_openid = "";
        HttpWebRequest hwr = WebRequest.Create(tokenUrl) as HttpWebRequest;
        hwr.Method = "POST";
        hwr.ContentType = "application/x-www-form-urlencoded";
        byte[] payload;
        payload = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(strpost)); //通过UTF-8编码
        hwr.ContentLength = payload.Length;
        Stream writer = hwr.GetRequestStream();
        writer.Write(payload, 0, payload.Length);
        writer.Close();
        var result = hwr.GetResponse() as HttpWebResponse; //此句是获得上面URl返回的数据
        var responseReader = new StreamReader(result.GetResponseStream());
        TagUserList codestate = JsonHelper.ToJson<TagUserList>(responseReader.ReadToEnd());
        if (codestate != null && codestate.data != null)
        {
            list.AddRange(codestate.data.openid);
        }
        return list;
    }
public class Data
{
    /// <summary>
    /// 
    /// </summary>
    public List<string> openid { get; set; }
}

public class TagUserList
{
    /// <summary>
    /// 
    /// </summary>
    public int count { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public Data data { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public string next_openid { get; set; }
}

4.给用户推送模板消息

   /// <summary>
    /// 公众号发送推送消息
    /// </summary>
    public async Task<bool> SendMessage(string strtoken, List<string> oplist) //string strtoken)
    {


        bool istrue = false;
        //根据access_token构建推送接口
        string sendUrl = $@"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={strtoken}";
        System.Net.Http.HttpClient sendclient = new System.Net.Http.HttpClient();
        MessageTemplateSendDto mts = new MessageTemplateSendDto();
        mts.template_id = ConfigurationManager.AppSettings["template_id"];
        //创建list 循环这里
        //必填 接收者openid
        foreach (var item in oplist)
        {
            mts.touser = item;
            //构建请求数据对象
            //必填 模板数据
            mts.data = new MessageTemplateSendDataDto
            {
                first = new MessageTemplateSendDataContentDto()
                {
                    value = "工作任务提示消息",
                    color = "#173177"
                },
                keyword1 = new MessageTemplateSendDataContentDto()
                {
                    value = "通知消息",
                    color = "#173177"
                },
                keyword2 = new MessageTemplateSendDataContentDto()
                {
                    value = "张三",
                    color = "#173177"
                },
                keyword3 = new MessageTemplateSendDataContentDto()
                {
                    value = "截止:02月14日,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
                    color = "#173177"
                },
                keyword4 = new MessageTemplateSendDataContentDto()
                {
                    value = DateTime.Now.ToShortDateString(),
                    color = "#173177"
                },
                remark = new MessageTemplateSendDataContentDto()
                {
                    value = "请及时查看",
                    color = "#173177"
                }
            };
            try
            {
                HttpWebRequest hwr = WebRequest.Create(sendUrl) as HttpWebRequest;
                hwr.Method = "POST";
                hwr.ContentType = "application/x-www-form-urlencoded";
                byte[] payload;
                payload = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(mts)); //通过UTF-8编码
                hwr.ContentLength = payload.Length;
                Stream writer = hwr.GetRequestStream();
                writer.Write(payload, 0, payload.Length);
                writer.Close();
                var result = hwr.GetResponse() as HttpWebResponse; //此句是获得上面URl返回的数据
                var responseReader = new StreamReader(result.GetResponseStream());
                WxOpenidCodeState codestate = JsonHelper.ToJson<WxOpenidCodeState>(responseReader.ReadToEnd());

                if (codestate.errcode == "0")
                {
                    istrue = true;
                }

            }
            catch (Exception e)
            {

                var t = e.Message;
                istrue = false;
            }
        }
        return istrue;
    }

public class MessageTemplateSendDto
{

    /// <summary>
    /// 必填
    /// 接收者openid
    /// </summary>
    public string touser { get; set; }

    /// <summary>
    /// 必填
    /// 模板ID
    /// </summary>
    public string template_id { get; set; }

    /// <summary>
    /// 必填
    /// 模板数据
    /// </summary>
    public MessageTemplateSendDataDto data { get; set; }

    /// <summary>
    /// 模板内容字体颜色,不填默认为黑色
    /// </summary>
    public string color { get; set; }

    /// <summary>
    /// 防重入id。对于同一个openid + client_msg_id, 只发送一条消息,10分钟有效,超过10分钟不保证效果。若无防重入需求,可不填
    /// </summary>
    public string client_msg_id { get; set; }

}

public class MessageTemplateSendDataDto
{
    public MessageTemplateSendDataContentDto first { get; set; }
    public MessageTemplateSendDataContentDto keyword1 { get; set; }
    public MessageTemplateSendDataContentDto keyword2 { get; set; }
    public MessageTemplateSendDataContentDto keyword3 { get; set; }
    public MessageTemplateSendDataContentDto keyword4 { get; set; }
    public MessageTemplateSendDataContentDto remark { get; set; }
}
public class MessageTemplateSendDataContentDto
{
    /// <summary>
    /// 文本内容
    /// </summary>
    public string value { get; set; }

    /// <summary>
    /// 文本颜色
    /// </summary>
    public string color { get; set; }
}
public class WxOpenidCodeState
{
 
    //返回code
    public string errcode { get; set; }

    // 返回消息
    public string errmsg { get; set; }
}

5.调用方法

        //获取access_token
        string strtoken = WXApi.GetAccessToken(appid, secret);
        //获取关注用户列表
        List<string> oplist = WXApi.GetTagUserList(strtoken);
        //发送模板消息
        Task<bool> istrue = SendMessage(strtoken, oplist);

6.配置文件

AppID和AppSecret及模板ID为了安全尽量别写在页面上,建议写在配置文件再在页面调用

配置文件

<appSettings>
    <!--微信的Token-->
    <add key="WeixinToken" value="weiphp" />
    <!--开发者ID(AppID)-->
    <add key="AppId" value="wxe9XXXXXXXXXX" />
    <!--开发者密匙-->
    <add key="AppSecret" value="804XXXXXXXXXXXXXXXXXXXXX" />
      <!--模板ID--> 
    <add key="template_id" value="0hXXXXXXXXXXXXXXXX" />
  </appSettings>

页面

    //公众号的appid|secret
    static string appid = ConfigurationManager.AppSettings["AppId"];
    static string secret = ConfigurationManager.AppSettings["AppSecret"];

一开始纠结了好久推送图文模板消息,研究了下发现

1.微信只提供发送文字模板功能,未提供推送图文的接口,图文消息只能通过后台的群发功能,但是一个月只能发4次。

2.开通高级群发功能,一个月可以有400次推送机会,但单个用户也只能接收4次。

3.可以通过第三方平台可以实现每天的服务号群发功能,但第三方平台服务内容包含无限次推送微信模版消息和群发图文给48小时内互动关注过的用户。

有推送图文需求的小伙伴的话,两个笨办法勉强能实现

  1. 开发完推送模板消息接口后,url再加个图片链接,用户应该可以点击模版消息跳转图片。

  1. 编辑完群发图文后存为草稿 ,然后输入用户的微信ID给用户发测试图文 (适合用户不多的情况)

值得注意的是:微信推送消息模板的ID来自于微信后台设置的所在行业,选择不同的行业属性会相应提供对应的消息模板,调用推送消息接口的时候去后台找到相应的模板ID即可。

再再一次值得注意的是:申请的测试号调试推送时,代码里的自定义模板消息不生效,切到正式环境就好啦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Vagrant】下载安装与基本操作

文章目录概述软件安装安装VirtualBox安装Vagrant配置环境用Vagrant创建一个VMVagrantfile文件配置常用命令概述 Vagrant是一个创建虚拟机的技术&#xff0c;是用来创建和管理虚拟机的工具&#xff0c;本身自己并不能创建管理虚拟机。创建和管理虚拟机必须依赖于其他的虚拟化技…

11 OpenCV图像识别之人脸识别

文章目录1 Eigenfaces1.1 建模流程1.2 示例代码2 Fisherfaces2.1 建模流程2.2 示例代码3 Local Binary Histogram3.1 建模流程3.2 示例代码OpenCV 提供了三种人脸识别方法&#xff1a;Eigenfaces Eigenfaces是一种基于PCA&#xff08;Principal Component Analysis&#xff0c…

多城市二手车买卖发布管理小程序开发

多城市二手车买卖发布管理小程序开发 功能特性: 为你介绍二手车微信小程序的功能特性。 车辆分类搜索&#xff0c;支持按品牌、售价、年龄、上牌时间、排量等筛选。 车源发布&#xff0c;支持用户一键发布二手车&#xff0c;平台审核上线&#xff0c;发布可编辑、删除等操作。…

【结构体版】通讯录

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前是C语言学习者 ✈️专栏&#xff1a;项目 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x…

扬帆优配|五千亿巨头一度涨停! 4天3倍,港股又现“狂飙”股!

周一&#xff0c;A股三大指数走势分化。到午间收盘&#xff0c;沪指震荡走高涨近1%&#xff0c;深证成指涨0.75%&#xff0c;创业板指继续弱势调整。 盘面上&#xff0c;钢铁、煤炭、大金融等权重板块团体走强&#xff0c;三大通讯运营商一同拉升&#xff0c;其间我国电信盘中一…

合作协议书合同怎么写?

合作协议书合同怎么写&#xff1f;以品牌推广的合作协议书合同为例&#xff0c;参考内容如下&#xff1a;业务合作协议甲方&#xff08;项目方&#xff09;&#xff1a;乙方&#xff08;客户推荐方&#xff09;&#xff1a;甲乙双方本着平等自愿、互惠互利原则&#xff0c;就结…

(十五)docker安装sentinel,客户端配置规则本地持久化

一、简介 操作系统&#xff1a;Linux CentOS 7.3 64位 docker版本&#xff1a;19.03.8 sentinel版本&#xff1a;1.8.0 二、实践 1、拉取镜像 docker pull bladex/sentinel-dashboard:1.8.0 2、运行容器 docker run --name sentinel \ -p 8858:8858 \ --privilegedtrue …

django项目实战三(django+bootstrap实现增删改查)进阶分页

目录 一、分页 1、修改case_list.html页面 2、修改views.py的case_list方法&#xff08;分页未封装&#xff09; 二、分页封装 1、新建类Pagination 2、修改views.py的case_list方法 三、再优化&#xff0c;实现搜索分页qing情况 四、优化其他查询页面实现分页和查询 五…

如何寻找SAP中的增强

文章目录0 简介1 寻找一代增强2 寻找二代增强2.2 在包里也可以看到2.3 在出口对象里输入包的名字也可以找到2.4 通过以下函数可以发现已有的增强2.5 也可以在cmod里直接找2.6 总结3 寻找第三代增强0 简介 在SAP中&#xff0c;对原代码的修改最不容易的是找增强&#xff0c;以下…

Springboot 整合 分布式定时任务 XXL-JOB

起因 恰逢周末&#xff0c; 最近公司接入了分布式定时任务&#xff0c;我是负责接入这块的&#xff0c;正好在网上想起了之前看过的分布式任务的文章&#xff0c;然后学习一下 各路框架发现看了很多框架比如 elasticjob 跟xxl-job不同的是&#xff0c;elasticjob是采用zookeepe…

Cesium 卫星轨迹、卫星通信、卫星过境,模拟数据传输。

起因&#xff1a;看了cesium官网卫星通信示例发现只有cmzl版本的&#xff0c;决定自己动手写一个。欢迎大家一起探讨&#xff0c;评论留言。 效果 全部代码在最后 起步 寻找卫星轨迹数据&#xff0c;在网站space-track上找的&#xff0c;自己注册账号QQ邮箱即可。 卫星轨道类…

stm32f407探索者开发板(十六)——串行通信原理讲解-UART

文章目录一、串口通信接口背景知识1.1 处理器与外部设备通信的两种方式1.2 按照数据传送方向1.3 是否带有时钟信号1.4 常见的串行通信接口二、STM32F4串口通信基础2.1 STM32的串口通信接口2.2 UART异步通信方式引脚连接方法2.3 UART异步通信方式引脚(STM32F407ZGT6)2.4 UART异步…

模拟物流快递系统程序设计-课后程序(JAVA基础案例教程-黑马程序员编著-第四章-课后作业)

【案例4-8】模拟物流快递系统程序设计 欢迎点赞收藏关注 【案例介绍】 案例描述 网购已成为人们生活的重要组成部分&#xff0c;当人们在购物网站中下订单后&#xff0c;订单中的货物就会在经过一系列的流程后&#xff0c;送到客户的手中。而在送货期间&#xff0c;物流管理…

实际项目角度优化App性能

前言&#xff1a;前年替公司实现了一个在线检疫App&#xff0c;接下来一年时不时收到该App的需求功能迭代&#xff0c;部分线下问题跟进。随着新冠疫情防控政策放开&#xff0c;该项目也是下线了。 从技术角度来看&#xff0c;有自己的独特技术处理特点。下面我想记录一下该App…

c++动态内存分布以及和C语言的比较

文章目录 前言一.c/c内存分布 C语言的动态内存管理方式 C内存管理方式 operator new和operator delete函数 malloc/free和new/delete的区别 定位new 内存泄漏的危害总结前言 c是在c的基础上开发出来的&#xff0c;所以关于内存管理这一方面是兼容c的&…

02- OpenCV绘制图形及图像算术变换 (OpenCV基础) (机器视觉)

知识重点 OpenCV用的最多的色彩空间是HSV. 方便OpenCV做图像处理img2 img.view() # 浅拷贝img3 img.copy() # 深拷贝split(mat) 分割图像的通道: b, g, r cv2.split(img) # b, g, r 都是数组merge((ch1, ch2, ch3)) 融合多个通道cvtColor(img, colorspace): 颜…

Centos7系统编译Hadoop3.3.4

1、背景 最近在学习hadoop&#xff0c;此篇文章简单记录一下通过源码来编译hadoop。为什么要重新编译hadoop源码&#xff0c;是因为为了匹配不同操作系统的本地库环境。 2、编译源码 2.1 下载并解压源码 [roothadoop01 ~]# mkdir /opt/hadoop [roothadoop01 ~]# cd /opt/had…

运动蓝牙耳机哪个牌子好性价比高、性价比高的运动蓝牙耳机推荐

如今耳机是我们生活中很常见的数码产品了&#xff0c;在街上看到跑步、骑行&#xff0c;室内健身房&#xff0c;都能看到大家人手一副耳机&#xff0c;运动耳机已经成为很多人的运动必备品&#xff0c;因大众佩戴耳机的种类和风格有所不同&#xff0c;这也造就了市场上琳琅满目…

RT-Thread SPI使用教程

RT-Thread SPI 使用教程 实验环境使用的是正点原子的潘多拉开发板。 SPI从机设备使用的是BMP280温湿度大气压传感器。 使用RT-Thread Studio搭建基础功能。 1. 创建工程 使用RT-Thread Studio IDE创建芯片级的工程。创建完成后&#xff0c;可以直接编译下载进行测试。 2.…

电源电路设计(一)(文末有易灵思核心板及下载线)

现在随着电子技术的高速发展&#xff0c;电子系统的应用领域也变得越来越广泛&#xff0c;电子设备的种类也在逐渐的不断更新、不断增多&#xff0c;电子设备与人们日常的工作、生活的关系也是日益密切。任何的电子设备都离不开安全有效的电源&#xff0c;电源是一切电力电子设…