当前位置: 代码迷 >> .NET相关 >> .NET微信大众号开发-6.0模板消息
  详细解决方案

.NET微信大众号开发-6.0模板消息

热度:354   发布时间:2016-04-24 02:48:10.0
.NET微信公众号开发-6.0模板消息

一.前言

    为了保证用户不受到骚扰,在开发者出现需要主动提醒、通知用户时,才允许开发者在公众平台网站中模板消息库中选择模板,选择后获得模板ID,再根据模板ID向用户主动推送提醒、通知消息。这个意思也就是,我们作为微信商户,不能主动的给用户推送消息,如果这个功能完全开放,那么用户有可能会受到大量的垃圾信息,为了做一个很好的控制,微信那边给我们开放了一个模板消息,通过模板消息我们可以友好的给用户发送一些相关的消息提醒。

二.开发前的准备

 1.0模板消息官方文档地址

 2.0查看我们的模板消息是否申请成功。申请成功后你可以看到如下图片:

三.开始编码

 一般我们在客户支付成功订单以后,会有一个支付的跳转页面,在我们前面的代码中也有相应的页面,代码如下:

<script type="text/javascript">               //调用微信JS api 支付               function jsApiCall()               {                   WeixinJSBridge.invoke(                   'getBrandWCPayRequest',                   <%=wxJsApiParam%>,//josn串                    function (res)                    {                      if (res.err_msg == "get_brand_wcpay_request:ok")                       {                           var OrderId=$("#OrderId").val();                           var orderProductName=$("#orderProductName").val();                           var orderMoneySum=$("#orderMoneySum").val();                            window.location.href="http://www.baidu.com/PaySkip.aspx?OrderId="+OrderId+"&orderMoneySum="+orderMoneySum+"&orderProductName="+orderProductName;//支付成功后的跳转页面                        }else                        {                          WeixinJSBridge.call('closeWindow');                        }                                              }                    );               }               function callpay()               {                   if (typeof WeixinJSBridge == "undefined")                   {                       if (document.addEventListener)                       {                           document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);                       }                       else if (document.attachEvent)                       {                           document.attachEvent('WeixinJSBridgeReady', jsApiCall);                           document.attachEvent('onWeixinJSBridgeReady', jsApiCall);                       }                   }                   else                   {                       jsApiCall();                   }               }                          </script>

    在上一篇,微信支付成功后,我们有这样一个支付跳转页面,在这个支付跳转页面里,我们可以写一些我们自己的业务逻辑,比如我们今天的主角,模板消息提醒:直接上代码

                    #region 发送支付成功的消息                    ErrorMessage errorMessage;                    string username = System.Configuration.ConfigurationManager.AppSettings["weixinid"].ToString();                    string templateId = System.Configuration.ConfigurationManager.AppSettings["templateId"].ToString();                    string first = "您已成功付费,欢迎下次光临。";                    string orderMoneySum = Request["orderMoneySum"] ?? "0";                    string orderProductName = Request["orderProductName"] ?? "有位停车付费系统测试";                    string Remark = "如有问题请致电400-6238-136或直接在微信留言,我们将第一时间为您服务!";                    Color color = Color.Red;                    Tuple<string, string, Color>[] data = new Tuple<string, string, Color>[]{                    new Tuple<string,string,Color>("first",first,Color.Blue),                    new Tuple<string,string,Color>("orderMoneySum",orderMoneySum,Color.Red),                    new Tuple<string,string,Color>("orderProductName",orderProductName,Color.Orange),                    new Tuple<string,string,Color>("Remark",Remark,Color.Green)                };                    long msgId = TemplateMessage.Send(username, openId, templateId, "", color, data, out errorMessage);                    if (errorMessage.IsSuccess)                    {                        Response.Write("errorMessage " + errorMessage.errmsg + "<br/>");                    }                    else                    {                        Response.Write("errorMessage.errmsg: " + errorMessage.errmsg + "<br/>");                    }                    #endregion

从上面的代码中我们可以看到TemplateMessage.Send()这个方法是我们发送消息的关键,我们来看看这个方法是怎样的:

        #region 发送模板消息        /// <summary>        /// 发送模板消息        /// </summary>        /// <param name="userName">公众号</param>        /// <param name="touser">接收消息的账号</param>        /// <param name="templateId">模板id</param>        /// <param name="detailUrl">详情地址</param>        /// <param name="topColor">顶端颜色</param>        /// <param name="data">数据</param>        /// <param name="errorMessage">返回发送是否成功</param>        /// <returns>返回消息id;如果发送失败,返回-1。</returns>        public static long Send(string userName, string touser, string templateId, string detailUrl, Color topColor,            Tuple<string, string, Color>[] data, out ErrorMessage errorMessage)        {            errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "");            long id = -1;            //校验参数            if (string.IsNullOrWhiteSpace(touser))            {                errorMessage.errmsg = "接收消息的账号不能为空。";                return id;            }            if (string.IsNullOrWhiteSpace(templateId))            {                errorMessage.errmsg = "模板id不能为空。";                return id;            }            if (data == null || data.Length == 0)            {                errorMessage.errmsg = "模板数据不能为空。";                return id;            }            foreach (Tuple<string, string, Color> item in data)            {                if (string.IsNullOrWhiteSpace(item.Item1) || string.IsNullOrWhiteSpace(item.Item2))                {                    errorMessage.errmsg = "模板数据不能为空。";                    return id;                }            }            //获取许可令牌            AccessToken token = AccessToken.Get(userName);            if (token == null)            {                errorMessage.errmsg = "获取许可令牌失败。";                return id;            }            string url = string.Format(urlForSending, token.access_token);            //生成待发送的数据            dynamic postData = new ExpandoObject();            postData.touser = touser;            postData.template_id = templateId;            postData.url = detailUrl ?? string.Empty;            postData.topcolor = Utility.GetColorString(topColor);            postData.data = new ExpandoObject();            IDictionary<string, object> dataDict = (IDictionary<string, object>)postData.data;            foreach (Tuple<string, string, Color> item in data)            {                dataDict.Add(item.Item1, new { value = item.Item2, color = Utility.GetColorString(item.Item3) });            }            string json = JsonConvert.SerializeObject(postData);            //发送数据            string responseContent;            if (!HttpHelper.Request(url, out responseContent, httpMethod, json))            {                errorMessage.errmsg = "提交数据到微信服务器失败。";                return id;            }            //解析结果            JObject jo = JObject.Parse(responseContent);            JToken jt;            if (jo.TryGetValue("errcode", out jt) && jo.TryGetValue("errmsg", out jt))            {                errorMessage.errcode = (int)jo["errcode"];                errorMessage.errmsg = (string)jo["errmsg"];                if (jo.TryGetValue("msgid", out jt))                    id = (long)jt;            }            else                errorMessage.errmsg = "解析返回结果失败。";            return id;        }         #endregion
 AccessToken token = AccessToken.Get(userName);获取许可令牌上一篇文章中我们已经说过这个类,这里就不多说了,HttpHelper帮助类的代码如下:
   /// <summary>    /// HttpHelper:http请求与响应辅助类    /// </summary>    public static class HttpHelper    {        /// <summary>        /// 向微信服务器发送请求时的编码        /// </summary>        public static readonly Encoding RequestEncoding = Encoding.UTF8;        /// <summary>        /// 微信服务器响应的编码        /// </summary>        public static readonly Encoding ResponseEncoding = Encoding.UTF8;        /// <summary>        /// 向微信服务器提交数据,并获取微信服务器响应的数据        /// </summary>        /// <param name="url">服务器地址</param>        /// <param name="responseData">返回响应数据</param>        /// /// <param name="httpMethod">http方法</param>        /// <param name="data">数据</param>        /// <returns>返回是否提交成功</returns>        public static bool Request(string url, out byte[] responseData,            string httpMethod = WebRequestMethods.Http.Get, byte[] data = null)        {            bool success = false;            responseData = null;            Stream requestStream = null;            HttpWebResponse response = null;            Stream responseStream = null;            MemoryStream ms = null;            try            {                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);                request.Method = httpMethod;                if (data != null && data.Length > 0)                {                    request.ContentLength = data.Length;                    requestStream = request.GetRequestStream();                    requestStream.Write(data, 0, data.Length);                }                response = (HttpWebResponse)request.GetResponse();                //由于微信服务器的响应有时没有正确设置ContentLength,这里不检查ContentLength                //if (response.ContentLength > 0)                {                    ms = new MemoryStream();                    responseStream = response.GetResponseStream();                    int bufferLength = 2048;                    byte[] buffer = new byte[bufferLength];                    int size = responseStream.Read(buffer, 0, bufferLength);                    while (size > 0)                    {                        ms.Write(buffer, 0, size);                        size = responseStream.Read(buffer, 0, bufferLength);                    }                    responseData = ms.ToArray();                }                success = true;            }            finally            {                if (requestStream != null)                    requestStream.Close();                if (responseStream != null)                    responseStream.Close();                if (ms != null)                    ms.Close();                if (response != null)                    response.Close();            }            return success;        }        /// <summary>        /// 向微信服务器提交数据,并获取微信服务器响应的数据        /// </summary>        /// <param name="url">服务器地址</param>        /// <param name="responseData">返回响应数据</param>        /// /// <param name="httpMethod">http方法</param>        /// <param name="data">数据</param>        /// <returns>返回是否提交成功</returns>        public static bool Request(string url, out byte[] responseData,            string httpMethod = WebRequestMethods.Http.Get, string data = null)        {            byte[] bytes = string.IsNullOrEmpty(data) ? null : RequestEncoding.GetBytes(data);            return Request(url, out responseData, httpMethod, (byte[])bytes);        }        /// <summary>        /// 向微信服务器提交数据,并获取微信服务器响应的内容        /// </summary>        /// <param name="url">服务器地址</param>        /// <param name="responseContent">返回响应内容</param>        /// /// <param name="httpMethod">http方法</param>        /// <param name="data">数据</param>        /// <returns>返回是否提交成功</returns>        public static bool Request(string url, out string responseContent,            string httpMethod = WebRequestMethods.Http.Get, byte[] data = null)        {            byte[] responseData;            responseContent = string.Empty;            bool success = Request(url, out responseData, httpMethod, data);            if (success && responseData != null && responseData.Length > 0)                responseContent = ResponseEncoding.GetString(responseData);            return success;        }        /// <summary>        /// 向微信服务器提交数据,并获取微信服务器响应的内容        /// </summary>        /// <param name="url">服务器地址</param>        /// <param name="responseContent">返回响应内容</param>        /// /// <param name="httpMethod">http方法</param>        /// <param name="data">数据</param>        /// <returns>返回是否提交成功</returns>        public static bool Request(string url, out string responseContent,            string httpMethod = WebRequestMethods.Http.Get, string data = null)        {            byte[] bytes = string.IsNullOrEmpty(data) ? null : RequestEncoding.GetBytes(data);            return Request(url, out responseContent, httpMethod, (byte[])bytes);        }        /// <summary>        /// 向微信服务器提交数据        /// </summary>        /// <param name="url">服务器地址</param>        /// /// <param name="httpMethod">http方法</param>        /// <param name="data">数据</param>        /// <returns>返回是否提交成功</returns>        public static bool Request(string url, string httpMethod = WebRequestMethods.Http.Get, byte[] data = null)        {            bool success = false;            Stream requestStream = null;            HttpWebResponse response = null;            try            {                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);                request.Method = httpMethod;                if (data != null && data.Length > 0)                {                    request.ContentLength = data.Length;                    requestStream = request.GetRequestStream();                    requestStream.Write(data, 0, data.Length);                }                response = (HttpWebResponse)request.GetResponse();                success = true;            }            finally            {                if (requestStream != null)                    requestStream.Close();                if (response != null)                    response.Close();            }            return success;        }        /// <summary>        /// 组合url,发送数据,然后返回响应字符串        /// </summary>        /// <param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>        /// <param name="userName">公众号</param>        /// <param name="urlParameters">参数</param>        /// <param name="httpMethod">执行请求的http方法</param>        /// <param name="data">请求的内容</param>        /// <returns>返回响应内容;如果请求失败,或者发生错误,返回空字符串</returns>        public static string RequestResponseContent(string urlFormat, string userName, IEnumerable<object> urlParameters = null, string httpMethod = WebRequestMethods.Http.Get, string data = null)        {            string responseContent = string.Empty;            AccessToken token = AccessToken.Get(userName);            if (token == null)                return responseContent;            string url;            if (urlParameters == null)                url = string.Format(urlFormat, token.access_token);            else            {                List<object> paramList = new List<object>(urlParameters);                paramList.Insert(0, token.access_token);                url = string.Format(urlFormat, paramList.ToArray());            }            HttpHelper.Request(url, out responseContent, httpMethod, (string)data);            return responseContent;        }        /// <summary>        /// 组合url,发送数据,然后返回响应的错误消息。        /// 注:错误消息不一定代表失败或者错误。        /// </summary>        /// <param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>        /// <param name="userName">公众号</param>        /// <param name="urlParameters">参数</param>        /// <param name="httpMethod">执行请求的http方法</param>        /// <param name="data">请求的内容</param>        /// <returns>返回响应的错误消息</returns>        public static ErrorMessage RequestErrorMessage(string urlFormat, string userName, IEnumerable<object> urlParameters = null, string httpMethod = WebRequestMethods.Http.Get, string data = null)        {            string responseContent = RequestResponseContent(urlFormat, userName, urlParameters, httpMethod, data);            if (string.IsNullOrWhiteSpace(responseContent))                return new ErrorMessage(ErrorMessage.ExceptionCode, "请求失败。");            else if (ErrorMessage.IsErrorMessage(responseContent))                return ErrorMessage.Parse(responseContent);            else                return new ErrorMessage(ErrorMessage.ExceptionCode, "解析响应失败。");        }        /// <summary>        /// 组合url,发送数据,然后返回结果。        /// 注:结果为需要解析的类。        /// </summary>        /// <typeparam name="T">返回结果的类型</typeparam>        /// <param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>        /// <param name="userName">公众号</param>        /// <param name="errorMessage">返回请求是否成功</param>        /// <param name="urlParameters">参数</param>        /// <param name="httpMethod">执行请求的http方法</param>        /// <param name="data">请求的内容</param>        /// <returns>返回结果;如果请求失败,或者发生错误,返回null。</returns>        public static T RequestParsableResult<T>(string urlFormat, string userName, out ErrorMessage errorMessage, IEnumerable<object> urlParameters = null, string httpMethod = WebRequestMethods.Http.Get, string data = null)            where T : IParsable, new()        {            T result = default(T);            errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "请求失败。");            string responseContent = RequestResponseContent(urlFormat, userName, urlParameters, httpMethod, data);            if (string.IsNullOrWhiteSpace(responseContent))                return result;            if (ErrorMessage.IsErrorMessage(responseContent))                errorMessage = ErrorMessage.Parse(responseContent);            else            {                try                {                    result = Utility.Parse<T>(responseContent);                    if (result != null)                        errorMessage = new ErrorMessage(ErrorMessage.SuccessCode, "请求成功。");                    else                        errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "解析失败。");                }                catch                {                    errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "解析失败。");                }            }            return result;        }        /// <summary>        /// 组合url,发送数据,然后返回结果。        /// 注:结果为已知的简单值类型。        /// </summary>        /// <typeparam name="T">返回结果的类型</typeparam>        /// <param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>        /// <param name="userName">公众号</param>        /// <param name="propertyNameInJson">返回结果在json中的键名</param>        /// <param name="errorMessage">返回请求是否成功</param>        /// <param name="urlParameters">参数</param>        /// <param name="httpMethod">执行请求的http方法</param>        /// <param name="data">请求的内容</param>        /// <returns>返回结果;如果请求失败,或者发生错误,返回default(T)。</returns>        public static T RequestValueTypeResult<T>(string urlFormat, string userName, string propertyNameInJson, out ErrorMessage errorMessage, IEnumerable<object> urlParameters = null, string httpMethod = WebRequestMethods.Http.Get, string data = null)            where T : struct        {            errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "请求失败。");            string responseContent = RequestResponseContent(urlFormat, userName, urlParameters, httpMethod, data);            return ConvertValueTypeResult<T>(responseContent, propertyNameInJson, out errorMessage);        }        /// <summary>        /// 获取值类型的结果        /// </summary>        /// <typeparam name="T">返回结果的类型</typeparam>        /// <param name="responseContent">响应内容</param>        /// <param name="propertyNameInJson">返回结果在json中的键名</param>        /// <param name="errorMessage">返回请求是否成功</param>        /// <returns>返回结果;如果请求失败,或者发生错误,返回default(T)。</returns>        private static T ConvertValueTypeResult<T>(string responseContent, string propertyNameInJson, out ErrorMessage errorMessage)            where T : struct        {            if (string.IsNullOrWhiteSpace(responseContent))            {                errorMessage = new ErrorMessage(ErrorMessage.ExceptionCode, "请求失败。");                return default(T);            }            if (ErrorMessage.IsErrorMessage(responseContent))                errorMessage = ErrorMessage.Parse(responseContent);            else                errorMessage = new ErrorMessage(ErrorMessage.SuccessCode, "请求成功。");            JObject jo = JObject.Parse(responseContent);            JToken jt;            if (jo.TryGetValue(propertyNameInJson, out jt))                return ConvertValueTypeResult<T>((string)jt);            else                return default(T);        }        /// <summary>        /// 获取值类型的结果        /// </summary>        /// <typeparam name="T">返回结果的类型</typeparam>        /// <param name="responseContent">响应内容</param>        /// <param name="propertyNameInJson">返回结果在json中的键名</param>        /// <param name="errorMessage">返回请求是否成功</param>        /// <returns>返回结果;如果请求失败,或者发生错误,返回default(T)。</returns>        private static T ConvertValueTypeResult<T>(string value)            where T : struct        {            Type type = typeof(T);            if (type.IsEnum)                return (T)Enum.Parse(type, value);            else if (type == typeof(sbyte))                return (T)(object)Convert.ToSByte(value);            else if (type == typeof(byte))                return (T)(object)Convert.ToByte(value);            else if (type == typeof(char))                return (T)(object)Convert.ToChar(value);            else if (type == typeof(short))                return (T)(object)Convert.ToInt16(value);            else if (type == typeof(ushort))                return (T)(object)Convert.ToUInt16(value);            else if (type == typeof(int))                return (T)(object)Convert.ToInt32(value);            else if (type == typeof(uint))                return (T)(object)Convert.ToUInt32(value);            else if (type == typeof(long))                return (T)(object)Convert.ToInt64(value);            else if (type == typeof(ulong))                return (T)(object)Convert.ToUInt64(value);            else if (type == typeof(float))                return (T)(object)Convert.ToSingle(value);            else if (type == typeof(double))                return (T)(object)Convert.ToDouble(value);            else if (type == typeof(decimal))                return (T)(object)Convert.ToDecimal(value);            else if (type == typeof(bool))                return (T)(object)Convert.ToBoolean(value);            else                throw new ArgumentException("不支持的值类型。");        }        /// <summary>        /// 向微信服务器提交数据        /// </summary>        /// <param name="url">服务器地址</param>        /// /// <param name="httpMethod">http方法</param>        /// <param name="data">数据</param>        /// <returns>返回是否提交成功</returns>        public static bool Request(string url, string httpMethod = WebRequestMethods.Http.Get, string data = null)        {            byte[] bytes = string.IsNullOrEmpty(data) ? null : RequestEncoding.GetBytes(data);            return Request(url, httpMethod, (byte[])bytes);        }        /// <summary>        /// 上传文件        /// </summary>        /// <param name="url">服务器地址</param>        /// <param name="filename">文件名(不包含路径)</param>        /// <param name="fileData">文件数据</param>        /// <param name="formData">表单数据</param>        /// <returns>返回服务器的响应字符串</returns>        public static string Upload(string url, string filename, byte[] fileData, NameValueCollection formData = null)        {            string responseContent = string.Empty;            if (string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(filename) || fileData == null || fileData.Length == 0)                return responseContent;            // 边界符            string boundary = "AaB03xAaB03x";            // 开始边界符            byte[] beginBoundary = Encoding.ASCII.GetBytes("--" + boundary + "\r\n");            // 结束符            byte[] endBoundary = Encoding.ASCII.GetBytes("--" + boundary + "--\r\n");            //换行            byte[] newLine = Encoding.ASCII.GetBytes("\r\n");            MemoryStream ms = null;            Stream stream = null;            HttpWebResponse response = null;            StreamReader sr = null;            try            {                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);                request.Method = WebRequestMethods.Http.Post;                request.ContentType = "multipart/form-data; boundary=" + boundary;                // 写入文件                string header = string.Format("Content-Disposition: form-data; filename=\"{0}\"\r\n" +                     "Content-Type: application/octet-stream\r\n\r\n",                     filename);                byte[] headerbytes = Encoding.UTF8.GetBytes(header);                ms = new MemoryStream();                ms.Write(beginBoundary, 0, beginBoundary.Length);                ms.Write(headerbytes, 0, headerbytes.Length);                ms.Write(fileData, 0, fileData.Length);                // 写入表单数据                if (formData != null && formData.Count > 0)                {                    var formItem = "\r\n--" + boundary +                                           "\r\nContent-Disposition: form-data; name=\"{0}\"" +                                           "\r\n\r\n{1}\r\n";                    foreach (string key in formData.Keys)                    {                        string value = formData[key];                        byte[] bytes = Encoding.UTF8.GetBytes(string.Format(formItem, key, value));                        ms.Write(bytes, 0, bytes.Length);                    }                }                //写入结束边界符                ms.Write(newLine, 0, newLine.Length);                ms.Write(endBoundary, 0, endBoundary.Length);                request.ContentLength = ms.Length;                stream = request.GetRequestStream();                stream.Write(ms.ToArray(), 0, (int)ms.Length);                //获取响应                response = (HttpWebResponse)request.GetResponse();                sr = new StreamReader(response.GetResponseStream(), HttpHelper.ResponseEncoding);                responseContent = sr.ReadToEnd();            }            finally            {                if (ms != null)                    ms.Close();                if (stream != null)                    stream.Close();                if (sr != null)                    sr.Close();                if (response != null)                    response.Close();            }            return responseContent;        }        /// <summary>        /// 上传文件        /// </summary>        /// <param name="url">服务器地址</param>        /// <param name="pathname">包含路径的文件名</param>        /// <param name="formData">表单数据</param>        /// <returns>返回服务器的响应字符串</returns>        public static string Upload(string url, string pathname, NameValueCollection formData = null)        {            string filename = Path.GetFileName(pathname);            byte[] data = null;            FileStream fs = null;            MemoryStream ms = null;            try            {                fs = new FileStream(pathname, FileMode.Open, FileAccess.Read);                ms = new MemoryStream();                int bufferLength = 2048;                byte[] buffer = new byte[bufferLength];                int size = fs.Read(buffer, 0, bufferLength);                while (size > 0)                {                    ms.Write(buffer, 0, size);                    size = fs.Read(buffer, 0, bufferLength);                }                data = ms.ToArray();            }            finally            {                if (fs != null)                    fs.Close();                if (ms != null)                    ms.Close();            }            return Upload(url, filename, data, formData);        }    }
HttpHelper

四.最终效果如下:

五.微信公众号开发系列导航

1.0初始微信公众号

2.0创建自定义菜单

3.0查询自定义菜单

4.0公众号消息处理

5.0微信支付

6.0模板消息

2楼麻将我会
收藏
Re: 枫伶忆
@麻将我会,感谢支持
1楼leewen
能提供解决方案源代码不?
Re: 枫伶忆
@leewen,我这个是根据官方的sdk修改的,下载地址https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
  相关解决方案