C#微信公众号开发

微信公众平台开发概述 | 微信开放文档 (qq.com)icon-default.png?t=L892https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.htmlPostMan9.0.5使用方法_无线智能模块 可视DIY智能专家-CSDN博客icon-default.png?t=L892https://blog.csdn.net/uaime/article/details/120639496?spm=1001.2014.3001.5501

一、创建测试账号

可以先申请一个开发者测试账号

用自己微信扫描后即可获得测试账号:

就有了appId 和 appsecret了,微信号在右上角。

二、获取access_token (这个access_token是通过appID 和 appsecret来生成的,只要是向微信服务器发送请求都需要带上这个access_token。)

打开 微信公众平台接口调试工具

程序生成access_token,解决2小时失效的问题,2小时候会重新生成一个。

access_token帮助类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.Linq;

namespace YangYiEcormerce.WeChat.Web.Common
{
    /// <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;
        }

    }
}

HttpUtility类:

/// <summary>
    ///Http帮助类
    /// </summary>
    public class HttpUtility
    {
        /// <summary>
        /// 发送请求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="data">数据</param>
        public static string SendHttpRequest(string url, string data)
        {
            return SendPostHttpRequest(url, "application/x-www-form-urlencoded", data);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetData(string url)
        {
            return SendGetHttpRequest(url, "application/x-www-form-urlencoded");
        }

        /// <summary>
        /// 发送请求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="method">方法(post或get)</param>
        /// <param name="method">数据类型</param>
        /// <param name="requestData">数据</param>
        public static string SendPostHttpRequest(string url, string contentType, string requestData)
        {
            WebRequest request = (WebRequest)HttpWebRequest.Create(url);
            request.Method = "POST";
            byte[] postBytes = null;
            request.ContentType = contentType;
            postBytes = Encoding.UTF8.GetBytes(requestData);
            request.ContentLength = postBytes.Length;
            using (Stream outstream = request.GetRequestStream())
            {
                outstream.Write(postBytes, 0, postBytes.Length);
            }
            string result = string.Empty;
            using (WebResponse response = request.GetResponse())
            {
                if (response != null)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
                        {
                            result = reader.ReadToEnd();
                        }
                    }

                }
            }
            return result;
        }

        /// <summary>
        /// 发送请求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="method">方法(post或get)</param>
        /// <param name="method">数据类型</param>
        /// <param name="requestData">数据</param>
        public static string SendGetHttpRequest(string url, string contentType)
        {
            WebRequest request = (WebRequest)HttpWebRequest.Create(url);
            request.Method = "GET";
            request.ContentType = contentType;
            string result = string.Empty;
            using (WebResponse response = request.GetResponse())
            {
                if (response != null)
                {
                    using (Stream stream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
                        {
                            result = reader.ReadToEnd();
                        }
                    }
                }
            }
            return result;
        }


    }

ParseJsonToXML方法: (需要引入Newtonsoft.Json.dll)

官网地址:Json.NET - Newtonsoft  (学习参考C# Newtonsoft.Json 高级用法 - 五维思考 - 博客园 (cnblogs.com)

使用案例1:通过配置json实现自定义菜单加载导航链接(c#)_无线智能模块 可视DIY智能专家-CSDN博客icon-default.png?t=L892https://blog.csdn.net/uaime/article/details/118101410?spm=1001.2014.3001.5501

 案例2:

arduino通过服务器文件进行实现远程控制_无线智能模块 可视DIY智能专家-CSDN博客icon-default.png?t=L892https://blog.csdn.net/uaime/article/details/115738151?spm=1001.2014.3001.5501

案例3:

ArduinoJson解析JSON字符串-物联网开发必备_无线智能模块 可视DIY智能专家-CSDN博客icon-default.png?t=L892https://blog.csdn.net/uaime/article/details/106044307?spm=1001.2014.3001.5501 

        /// <summary>
        /// 将Json转化为XML
        /// </summary>
        /// <param name="json"></param>
        /// <param name="rootName"></param>
        /// <returns></returns>
        public static XDocument ParseJsonToXML(string json, string rootName)
        {
            return JsonConvert.DeserializeXNode(json, rootName);
        }

程序调用获取access_token:

string access_token = Common.AccessTokenHelp.AccessToken;

 

三、创建菜单:

还是使用调试工具来创建:

JSON格式菜单内容:下面创建的都是一级菜单,更多详细的菜单创建,参考说明文档>>

 

{
    "button": [
        {
            "name": "9i物联", 
            "type": "view", 
            "url": "http://jiuai.blog.csdn.net"  //点击菜单访问网址
        },
        {
            "name": "9i分享", 
            "type": "scancode_push", 
            "key": "FangweiScan"      //点击调用微信二维码扫描,是网址直接访问,是文本则显示文本内容
        },
        {
            "name": "关于9i", 
            "type": "click", 
            "key": "OrderQuery"       //点击出发click事件,向我们配置的API地址进行请求
        }
    ]
}

 请求成功后,取消微信号关注并退出微信,重新进入关注,应该就可以看到添加好的文档了。

想删除重新创建菜单,调用菜单删除就可以了。

 

四、开发接口、处理文本和事件 (当用户使用微信发送消息或者单击菜单出发事件,就会想配置的API发送请求,API进行处理响应)    消息回复参考文档>>

接口是一个一般处理程序: 

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;
            }
        }
    }
}

接受/发送消息帮助类

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>";
            }
        }


    }
}

五、发送模板消息  (官方说明文档>>)

在公众平台创建模板:

发送模板消息帮助类:

 

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);
        }

    }

发送模板消息调用:

var data = new
     {
       first = new
      {
         value = "恭喜你购买成功",
          color = "#173177"
      }
     };
      string templateId = "ibrQIRAaFkbeRNKpj9SbuJ0Rgs6q1ZTpsNkdf31lZwM";
      TemplateMessage.SendTemplate(FromUserName.InnerText, templateId, data);

六、接口开发完成,配置接口信息

配置验证通过后,用户发消息或事件,接口拿到信息就可以做出处理反馈了。

接口URL验证:

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");
                }
            }
        }

 验证签名方法:

/// <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加密算法:

        /// <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();
        }

如果填写的Token和接口中的Token不一致就会验证失败

这些认真看完,并动手操作一遍,应该不是问题。更多参考内容请参阅以下

 文章参考:

1、C#开发微信公众平台-就这么简单(附Demo) - jack_Meng - 博客园 (cnblogs.com)

2、C#微信公众号开发入门教程 - IT浪潮之巅 - 博客园 (cnblogs.com) 

3、C#开发微信门户及应用(1)--开始使用微信接口 - 伍华聪 - 博客园 (cnblogs.com)  包含系列

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

 C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答

C#开发微信门户及应用(2)--微信消息的处理和应答

C#开发微信门户及应用(1)--开始使用微信接口

4、用c#开发微信 系列汇总 - 疯吻IT - 博客园 (cnblogs.com)  包含系列

5、微信 - 随笔分类 - Wuya - 博客园 (cnblogs.com) 

.net微信公众号开发——模板消息 

​​​​​​.net微信公众号开发——群发消息.net微信公众号开发——消息与事件

.net微信公众号开发——基础接口 

.net微信公众号开发——快速入门 

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值