一、准备工作
微信公众平台:https://mp./
申请测试账号:https://mp./debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
微信推送消息模板不需要发布服务器,也不需要填写授权回调域名,只需要两个微信后台的参数
在公众号后台设置与开发——>基本配置中获取AppID、AppSecret
二、实现思路
我的需求是给特定分组的用户推送模板消息,分为以下4步
获取access_token(把token存缓存,不用每次请求)
获取公众号的所有标签,得到目标分组
获取指定标签下的粉丝列表,得到用户的openid
给粉丝推送模板消息
三、后端代码
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赋给resultif (tokenCache.ContainsKey(appid)){result = tokenCache[appid];}//不存在则获取tokenif (result == null){result = GetToken(appid, secret);result.CreateTime = DateTime.Now;tokenCache.Add(appid, result);}//判断是否在有效期内,过期重新获取token 给10s延迟时间else if (pare(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./cgi-bin/token?grant_type={0}&appid={1}&secret={2}", grant_type, appid, secret);var wc = new WebClient();var strReturn = wc.DownloadString(tokenUrl);//得到tokenAccess_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./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./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./cgi-bin/message/template/send?access_token={strtoken}";.Http.HttpClient sendclient = new .Http.HttpClient();MessageTemplateSendDto mts = new MessageTemplateSendDto();mts.template_id = ConfigurationManager.AppSettings["template_id"];//创建list 循环这里//必填 接收者openidforeach (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{//返回codepublic string errcode { get; set; }// 返回消息public string errmsg { get; set; }}
5.调用方法
//获取access_tokenstring 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|secretstatic string appid = ConfigurationManager.AppSettings["AppId"];static string secret = ConfigurationManager.AppSettings["AppSecret"];
一开始纠结了好久推送图文模板消息,研究了下发现
1.微信只提供发送文字模板功能,未提供推送图文的接口,图文消息只能通过后台的群发功能,但是一个月只能发4次。
2.开通高级群发功能,一个月可以有400次推送机会,但单个用户也只能接收4次。
3.可以通过第三方平台可以实现每天的服务号群发功能,但第三方平台服务内容包含无限次推送微信模版消息和群发图文给48小时内互动关注过的用户。
有推送图文需求的小伙伴的话,两个笨办法勉强能实现
开发完推送模板消息接口后,url再加个图片链接,用户应该可以点击模版消息跳转图片。
编辑完群发图文后存为草稿 ,然后输入用户的微信ID给用户发测试图文 (适合用户不多的情况)
值得注意的是:微信推送消息模板的ID来自于微信后台设置的所在行业,选择不同的行业属性会相应提供对应的消息模板,调用推送消息接口的时候去后台找到相应的模板ID即可。
再再一次值得注意的是:申请的测试号调试推送时,代码里的自定义模板消息不生效,切到正式环境就好啦。