首先打開開發文檔:php
微信公衆號開發者文檔:http://mp.weixin.qq.com/wiki/home/index.htmlhtml
1、建立測試帳號算法
能夠先申請一個開發者測試帳號json
用本身微信掃描後便可得到測試帳號:c#
就有了appId 和 appsecret了,微信號在右上角。api
2、獲取access_token (這個access_token是經過appID 和 appsecret來生成的,只要是向微信服務器發送請求都須要帶上這個access_token。)服務器
打開 微信公衆平臺接口調試工具 微信
程序生成access_token,解決2小時失效的問題,2小時候會從新生成一個。微信開發
access_token幫助類:app
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)
/// <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;
3、建立菜單:
仍是使用調試工具來建立:
JSON格式菜單內容:下面建立的都是一級菜單,更多詳細的菜單建立,參考說明文檔>>
{ "button": [ { "name": "商城", "type": "view", "url": "http://shop.com" //點擊菜單訪問網址 }, { "name": "防僞掃描", "type": "scancode_push", "key": "FangweiScan" //點擊調用微信二維碼掃描,是網址直接訪問,是文本則顯示文本內容 }, { "name": "訂單查詢", "type": "click", "key": "OrderQuery" //點擊出發click事件,向咱們配置的API地址進行請求 } ] }
請求成功後,取消微信號關注並退出微信,從新進入關注,應該就能夠看到添加好的文檔了。
想刪除從新建立菜單,調用菜單刪除就能夠了。
效果:
4、開發接口、處理文本和事件 (當用戶使用微信發送消息或者單擊菜單出發事件,就會想配置的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>"; } } } }
5、發送模板消息 (官方說明文檔>>)
在公衆平臺建立模板:
發送模板消息幫助類:
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);
效果:
6、接口開發完成,配置接口信息
配置驗證經過後,用戶發消息或事件,接口拿到信息就能夠作出處理反饋了。
接口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不一致就會驗證失敗,就能夠防止其餘人盜用你的接口了。
7、源碼下載
8、相關學習資源推薦:
2.C#開發微信門戶及應用 (比較全,winform實現,但沒完整源碼)
時間匆忙,寫的不是很詳細,有時間再慢慢完善。