Loading... ## C#微信公众号开发 ### 一》 准备 - [【开发文档】](https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html) - [【微信公众号测试接口】](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) 用自己的微信扫码登陆,然后就可以获取就有了appId 和 appsecret。 ![](https://img-blog.csdnimg.cn/5b54e5cdfdf346faa845c46b90952b5e.png) ### 二》获取access_token 打开 [微信公众平台接口调试工具](https://mp.weixin.qq.com/debug/) ![](https://img-blog.csdnimg.cn/aa5468e8d76a4451982d2608524e9ccc.png) 这个access_token是通过appID 和 appsecret来生成的,access_token 有效期为 **两个小时**(7200秒),一天可以获取2000次,只要是向微信服务器发送请求都需要带上这个access_token ```csharp /// <summary> /// AccessToken帮助类 /// </summary> public class AccessTokenHelp { //填写自己微信的秘钥 private static string appId = System.Configuration.ConfigurationManager.AppSettings["WeChatAppId"]; private static string appSecret = System.Configuration.ConfigurationManager.AppSettings["WeChatAppSecret"]; private static DateTime GetAccessToken_Time; /// <summary> /// 过期时间为7200秒 /// </summary> private static int Expires_Period = 7200; /// <summary> /// /// </summary> private static string mAccessToken; /// <summary> /// /// </summary> public static string AccessToken { get { //如果为空,或者过期,需要重新获取 if (string.IsNullOrEmpty(mAccessToken) || HasExpired()) { //获取 mAccessToken = GetAccessToken(appId, appSecret); } return mAccessToken; } } /// <summary> /// /// </summary> /// <param name="appId"></param> /// <param name="appSecret"></param> /// <returns></returns> private static string GetAccessToken(string appId, string appSecret) { string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, appSecret); string result = HttpUtility.GetData(url); XDocument doc = CommonHelp.ParseJsonToXML(result, "root"); XElement root = doc.Root; if (root != null) { XElement access_token = root.Element("access_token"); if (access_token != null) { GetAccessToken_Time = DateTime.Now; if (root.Element("expires_in") != null) { Expires_Period = int.Parse(root.Element("expires_in").Value); } return access_token.Value; } else { GetAccessToken_Time = DateTime.MinValue; } } return null; } /// <summary> /// 判断Access_token是否过期 /// </summary> /// <returns>bool</returns> private static bool HasExpired() { if (GetAccessToken_Time != null) { //过期时间,允许有一定的误差,一分钟。获取时间消耗 if (DateTime.Now > GetAccessToken_Time.AddSeconds(Expires_Period).AddSeconds(-60)) { return true; } } return false; } ``` 通过 `string access_token = Common.AccessTokenHelp.AccessToken;` 就可以获取accesstoken ### 三》创建菜单 通过测试接口来处理 ![](https://img-blog.csdnimg.cn/eed2dd91747f4a6e9957c23c2bb072fa.png)![](https://img-blog.csdnimg.cn/f814b078e18a4aa1a32a657ba5775be8.png) ```csharp { "button": [ { "name": "迷恋自留地", "type": "view", "url": "https://hunji.xyz" //点击菜单访问网址 }, { "name": "防伪扫描", "type": "scancode_push", "key": "FangweiScan" //点击调用微信二维码扫描,是网址直接访问,是文本则显示文本内容 }, { "name": "订单查询", "type": "click", "key": "OrderQuery" //点击出发click事件,向我们配置的API地址进行请求 } ] } ``` 扫码关注公众号就ok,更多的按钮参考:[微信开发文档](https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Personalized_menu_interface.html) ![](https://img-blog.csdnimg.cn/f9640d29f59845bc981d198a75c4ca21.png) 请求成功后,取消微信号关注并退出微信,重新进入关注,应该就可以看到添加好的文档了。 想删除重新创建菜单,调用菜单删除就可以了。 ![](https://img-blog.csdnimg.cn/92770aad11974886bf5302ce680e943f.png) ### 四》开发接口、处理文本和事件 当用户使用微信发送消息或者单击菜单出发事件,就会向配置的API发送请求,API进行处理响应 : [消息回复参考文档](https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html) c#一般多半是一般处理程序,目前是net6都已经出来了,处理方式大差不差,但还是有些不一样的地方。net5 具体的代码实现请查阅下篇文章,在这里先埋个坑 ```csharp using System; using System.Collections.Generic; using System.Web; using System.IO; using System.Text; using System.Web.Security; using System.Xml; namespace weixin_api { /// <summary> /// interfaceTest 的摘要说明 /// </summary> public class interfaceTest : IHttpHandler { public void ProcessRequest(HttpContext param_context) { string postString = string.Empty; //用户发送消息或点击等事件一般都是POST过来,微信服务器向接口发送POST请求,根据请求我们进行处理反馈 if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST") { using (Stream stream = HttpContext.Current.Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); postString = Encoding.UTF8.GetString(postBytes); Handle(postString); } } else { //第一次配置接口地址的时候,微信服务器会向接口发送一个GET请求来验证你的接口地址 InterfaceTest(); } } /// <summary> /// 处理信息并应答 /// </summary> private void Handle(string postStr) { messageHelp help = new messageHelp(); string responseContent = help.ReturnMessage(postStr); HttpContext.Current.Response.ContentEncoding = Encoding.UTF8; HttpContext.Current.Response.Write(responseContent); } //成为开发者url测试,返回echoStr public void InterfaceTest() { string token = "token"; if (string.IsNullOrEmpty(token)) { return; } //微信服务器会将下面几个参数发送到接口,接口这边返回接收到的echoStr就说明验证通过, //主要为了防止别人盗用你的接口,我这边没做逻辑判断直接返回接收到的echoStr来通过验证 string echoString = HttpContext.Current.Request.QueryString["echoStr"]; string signature = HttpContext.Current.Request.QueryString["signature"]; string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"]; if (!string.IsNullOrEmpty(echoString)) { HttpContext.Current.Response.Write(echoString); HttpContext.Current.Response.End(); } } public bool IsReusable { get { return false; } } } } ``` 接受/发送消息帮助类 ```csharp using System; using System.Collections.Generic; using System.Web; using System.IO; using System.Text; using System.Web.Security; using System.Xml; namespace weixin_api { /// <summary> /// 接受/发送消息帮助类 /// </summary> public class messageHelp { //返回消息 public string ReturnMessage(string postStr) { string responseContent = ""; XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(new System.IO.MemoryStream(System.Text.Encoding.GetEncoding("GB2312").GetBytes(postStr))); XmlNode MsgType = xmldoc.SelectSingleNode("/xml/MsgType"); if (MsgType != null) { switch (MsgType.InnerText) { case "event": responseContent = EventHandle(xmldoc);//事件处理 break; case "text": responseContent = TextHandle(xmldoc);//接受文本消息处理 break; default: break; } } return responseContent; } //事件 public string EventHandle(XmlDocument xmldoc) { string responseContent = ""; XmlNode Event = xmldoc.SelectSingleNode("/xml/Event"); XmlNode EventKey = xmldoc.SelectSingleNode("/xml/EventKey"); XmlNode ToUserName = xmldoc.SelectSingleNode("/xml/ToUserName"); XmlNode FromUserName = xmldoc.SelectSingleNode("/xml/FromUserName"); XmlNode ScanResult = xmldoc.SelectSingleNode("/xml/ScanCodeInfo/ScanResult"); if (Event != null) { //菜单单击事件 if (Event.InnerText.Equals("CLICK")) { if (EventKey.InnerText.Equals("OrderQuery"))//点击订单查询 这个OrderQuery就是菜单里面的key { responseContent = string.Format(ReplyType.Message_Text, FromUserName.InnerText, ToUserName.InnerText, DateTime.Now.Ticks, "正在开发中,敬请期待!"); } } else if (Event.InnerText.Equals("scancode_waitmsg")) //扫码推事件且弹出“消息接收中”提示框的事件推送 { if (EventKey.InnerText.Equals("FangweiScan")) //点击防伪扫描 { //....处理返回逻辑 } } } return responseContent; } //接受文本消息 public string TextHandle(XmlDocument xmldoc) { string responseContent = ""; XmlNode ToUserName = xmldoc.SelectSingleNode("/xml/ToUserName"); XmlNode FromUserName = xmldoc.SelectSingleNode("/xml/FromUserName"); XmlNode Content = xmldoc.SelectSingleNode("/xml/Content"); if (Content != null) { //回复文本信息 responseContent = string.Format(ReplyType.Message_Text, FromUserName.InnerText, ToUserName.InnerText, DateTime.Now.Ticks, "欢迎使用微信公共账号,您输入的内容为:" + Content.InnerText); } return responseContent; } //写入日志 public void WriteLog(string text) { StreamWriter sw = new StreamWriter(HttpContext.Current.Server.MapPath(".") + "\\log.txt", true); sw.WriteLine(text); sw.Close(); } } //回复类型 public class ReplyType { /// <summary> /// 普通文本消息 /// </summary> public static string Message_Text { get { return @"<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[{3}]]></Content> </xml>"; } } /// <summary> /// 图文消息主体 /// </summary> public static string Message_News_Main { get { return @"<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[news]]></MsgType> <ArticleCount>{3}</ArticleCount> <Articles> {4} </Articles> </xml> "; } } /// <summary> /// 图文消息项 /// </summary> public static string Message_News_Item { get { return @"<item> <Title><![CDATA[{0}]]></Title> <Description><![CDATA[{1}]]></Description> <PicUrl><![CDATA[{2}]]></PicUrl> <Url><![CDATA[{3}]]></Url> </item>"; } } } } ``` ### 五》发送模板消息 首先在后台添加测试的模板,获取到消息模板的id ![](https://img-blog.csdnimg.cn/7f976098689a40aea8f63e356f6c1545.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/8bcc0e3085784a9ead4dea96d28ac870.png) 发送模板消息帮助类: ```csharp public class TemplateMessage { static JavaScriptSerializer Jss = new JavaScriptSerializer(); /// <summary> /// 给指定的用户发送模板消息 /// </summary> /// <param name="openId">用户标识openid</param> /// <param name="templateId">对应的模板id</param> /// <param name="data">对应模板的参数</param> /// <param name="url">点击对应消息弹出的地址</param> /// <param name="topcolor">颜色</param> /// <returns>返回json数据包</returns> public static string SendTemplate(string openId, string templateId, object data, string url, string topcolor = "#173177") { string access_token = AccessTokenHelp.AccessToken; var msgData = new { touser = openId, template_id = templateId, topcolor = topcolor, url = url, data = data }; string postData = Jss.Serialize(msgData); return HttpUtility.SendHttpRequest("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token, postData); } /// <summary> /// 给指定的用户发送模板消息 /// </summary> /// <param name="openId">用户标识openid</param> /// <param name="templateId">对应的模板id</param> /// <param name="data">对应模板的参数</param> /// <param name="topcolor">颜色</param> /// <returns>返回json数据包</returns> public static string SendTemplate(string openId, string templateId, object data, string topcolor = "#173177") { string access_token = AccessTokenHelp.AccessToken; var msgData = new { touser = openId, template_id = templateId, topcolor = topcolor, data = data }; string postData = Jss.Serialize(msgData); return HttpUtility.SendHttpRequest("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token, postData); } } ``` 发送模板消息调用: ```csharp var data = new { first = new { value = "恭喜你购买成功", color = "#173177" } }; string templateId = "ibrQIRAaFkbeRNKpj9SbuJ0Rgs6q1ZTpsNkdf31lZwM"; TemplateMessage.SendTemplate(FromUserName.InnerText, templateId, data); ``` ![](https://img-blog.csdnimg.cn/d679b99e9b984b4290906ba49bf23a84.png) [参考链接](https://www.cnblogs.com/zxx193/p/4940292.html) ### 六》接口验证 配置验证通过后,用户发消息或事件,接口拿到信息就可以做出处理反馈了 ```csharp public void ProcessRequest(HttpContext context) { string postString = string.Empty; if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST") { using (Stream stream = HttpContext.Current.Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); postString = Encoding.UTF8.GetString(postBytes); Handle(postString); } } else { //验证签名 if (CheckSignature()) { HttpContext.Current.Response.Write(HttpContext.Current.Request.QueryString["echoStr"]); } else { HttpContext.Current.Response.Write("error"); } } } ``` 签名方法 ```csharp /// <summary> /// 检查签名 /// </summary> /// <param name="request"></param> /// <returns></returns> private bool CheckSignature() { string token = System.Configuration.ConfigurationManager.AppSettings["WeChatToken"]; string signature = HttpContext.Current.Request.QueryString["signature"]; string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"]; List<string> list = new List<string>(); list.Add(token); list.Add(timestamp); list.Add(nonce); //排序 list.Sort(); //拼串 string input = string.Empty; foreach (var item in list) { input += item; } //加密 string new_signature = CommonHelp.SHA1Encrypt(input); //验证 if (new_signature == signature) { return true; } else { return false; } } ``` 签名中的SHA1Encrypt加密算法 ```csharp /// <summary> /// SHA1加密 /// </summary> /// <param name="intput">输入字符串</param> /// <returns>加密后的字符串</returns> public static string SHA1Encrypt(string intput) { byte[] StrRes = Encoding.Default.GetBytes(intput); HashAlgorithm mySHA = new SHA1CryptoServiceProvider(); StrRes = mySHA.ComputeHash(StrRes); StringBuilder EnText = new StringBuilder(); foreach (byte Byte in StrRes) { EnText.AppendFormat("{0:x2}", Byte); } return EnText.ToString(); } ``` 推荐公众号:net分享 ![我的公众号](https://img-blog.csdnimg.cn/20210517111144223.png) ![](https://img-blog.csdnimg.cn/img_convert/4d033c96fd8aaedb523b546d3c39f92f.png#pic_center) 最后修改:2023 年 08 月 10 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 1 如果觉得我的文章对你有用,请随意赞赏