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

一、准备工作

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

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

鉴于一些兄弟需要jsonhelper,特放百度网盘,需要自取

链接:https://pan.baidu.com/s/1fceTTPQUqiYHL6QU7s1row

提取码:gv4v

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

在公众号后台设置与开发——>基本配置中获取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 PostTage
{
    //最后一个用户的openid 
    public int tagid { get; set; }
    //第一个拉取的OPENID,不填默认从头开始拉取 
    public string next_openid { get; set; }
}

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即可。

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

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值