微信公衆帳號開發之N個坑(一)

 

 

我這人幹活沒有前奏,喜歡直接開始。完了,寶寶已經被大家帶污了。。html

微信公衆帳號開發文檔,官方版(https://mp.weixin.qq.com/wiki),相信我,我已經無力吐槽寫這個文檔的人了,我真心想雜碎這個鍵盤,可是下手以後才發現,原來鍵盤是我本身花錢買的。。。。尷尬了。json

廢話不說,直接說怎麼部署,怎麼開發。api

首先,你得有一個公衆平臺帳號,好了,開始計坑。安全

第一坑,不要覺得不是企業號就不能開發了,能夠申請測試號的,比所謂的訂閱號接口多多了。服務器

 

進入後臺管理以後,點擊開發者工具,能夠看到公衆平臺測試帳號,直接進入便可。開始填寫本身的配置。微信

 

注意塗鴉部分,這部分是程序中必需要配置的東東,若是沒有配置的話,這是必定不成功的。app

第二坑,固然,你這麼配置也必定是不成功的,不要問我爲何。沒圖說個幾把。。。(https://mp.weixin.qq.com/wiki)工具

請不要認爲企鵝帝鬧着玩,這是真的,必須是80端口,其實也就發佈一個域名的網站便可。由於域名的網站都是80端口出來的,繼續說正題。post

企鵝帝告訴咱們,要用微信帳號,必須有一臺服務器,而後配置咱們發佈的網站就好了,請注意,token是本身設定的,這個不是自動生成的,本身設定。。URL就是咱們發佈的網站名稱測試

第三坑,網站不發佈,接口配置信息是永遠配置不過去的,記住,是永遠。

JS接口安全域名,這個請直接參考文檔(http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html)。

JS接口安全域名的目的是爲了下載圖片,調用微信圖片接口等等,好比當你須要調用攝像頭的時候,或者說說須要上傳照片的時候,這時候就須要JS安全接口了,關於具體的內容暫不作詳細描述。

在微信公衆帳號測試號的版本後臺中,有一個體驗接口權限表裏面的這一項也是必須配置的。也不算是必須配置,而是這個接口能夠獲取到微信用戶的部分信息。值得提醒的是每一個公衆帳號對應的每一個ID是惟一的,也就是說,即使網站內網不變,若是換了公衆號,那麼,這時候的微信公衆號的數據是不能共通的,只是相對公共號惟一。

第四坑,申請微信網頁受權的時候,這裏的網頁受權用戶基本信息,這個自己沒問題,可是沒有提示就有問題了。

這裏的網址,請注意,必定是不含www的,並且後面沒有反斜槓,也就是說這裏的網址的回調格式是  abc.de.com 或者直接是一級域名也能夠,OK,請記住這個格式,必須這麼幹。好了,服務器暫且這樣,暫開始用代碼說話。

首先從服務器驗證提及。這個在官網是有例子的,不過是PHP的,其實說白了首先就是驗證一個隨機數,而後在POST的狀況下,檢測返回值便可。直接上代碼

        public ActionResult Index()
        {
            if (Request.HttpMethod.ToLower() == "post")
            {
                if (CheckSignature())//驗證服務器是否經過
                {
                    GetMenuList();//加載菜單
                }
                else
                {
                    Response.Write("<h1>Oh</h1><h2>咱們相遇在火星吧!!!</h2>");
                    Response.End();
                }
            }
            else
            {
                CheckWechat();
            }
            return View();
        }

        /// <summary>
        /// 返回隨機數表示驗證成功
        /// </summary>
        private void CheckWechat()
        {
            if (string.IsNullOrEmpty(Request.QueryString["echoStr"]))
            {
                Response.Write("消息並不是來自微信");
                Response.End();
            }
            string echoStr = Request.QueryString["echoStr"];
            if (CheckSignature())
            {
                Response.Write(echoStr);
                Response.End();
            }
        }
	/// <summary>
        /// 驗證微信簽名
        /// </summary>
        /// <returns></returns>
        /// 將token、timestamp、nonce三個參數進行字典序排序
        /// 將三個參數字符串拼接成一個字符串進行sha1加密
        /// 開發者得到加密後的字符串可與signature對比,標識該請求來源於微信。
        private bool CheckSignature()
        {
            string signature = Convert.ToString(Request["signature"]);
            string timestamp = Convert.ToString(Request["timestamp"]);
            string nonce = Convert.ToString(Request["nonce"]);
            string[] ArrTmp = { Token, timestamp, nonce };
            Array.Sort(ArrTmp);   //字典排序  
            string tmpStr = string.Join("", ArrTmp);
            tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
            tmpStr = tmpStr.ToLower();
            if (tmpStr == signature)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

 而後,公衆平臺在有權限的狀況下是自定義菜單的,可是一旦開始自定義菜單,原來的手動編輯的菜單是不能用的,也就是說,若是服務器驗證經過,那麼必須用本身的代碼控制。

咱們一塊兒來看GetMenuList()這個方法,這個其實很簡單的,就是隨便憑藉一個JSON格式字符串。而後調用微信的接口便可。 

  

  public void GetMenuList()
        {
            string menu = "";
            menu = @" {  
     ""button"":[  
     {    
          ""type"":""click"",  
          ""name"":""你好!"",  
          ""key"":""hello""  
      },  
      {  
           ""type"":""view"",  
           ""name"":""公司簡介"",  
           ""url"":""http://www.xnfhtech.com/index.html""  
      },  
      {  
           ""name"":""產品介紹"",  
           ""sub_button"":[  
            {  
               ""type"":""click"",  
               ""name"":""產品1"",  
                ""key"":""p1""  
            },  
            {  
               ""type"":""click"",  
               ""name"":""產品2"",  
               ""key"":""p2""  
            }]  
       }]  
 }  
";

            string access_token = IsExistAccess_Token();
            string i = MenuCreate(menu, access_token);
            Response.Write(i);
        }

        public string MenuCreate(string MenuJson, string access_token)
        {
            JavaScriptSerializer Jss = new JavaScriptSerializer();
            string setMenuUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}";
            setMenuUrl = string.Format(setMenuUrl, access_token);//獲取token、拼湊url
            string respText = WebRequestPostOrGet(setMenuUrl, MenuJson);
            Dictionary<string, object> respDic = (Dictionary<string, object>)Jss.DeserializeObject(respText);
            return respDic["errcode"].ToString();//返回0發佈成功
        }

        /// <summary>
        /// Post/get 提交調用抓取
        /// </summary>
        /// <param name="url">提交地址</param>
        /// <param name="param">參數</param>
        /// <returns>string</returns>
        public string WebRequestPostOrGet(string sUrl, string sParam)
        {
            byte[] bt = System.Text.Encoding.UTF8.GetBytes(sParam);

            Uri uriurl = new Uri(sUrl);
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uriurl);//HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url + (url.IndexOf("?") > -1 ? "" : "?") + param);
            req.Method = "Post";
            req.Timeout = 120 * 1000;
            req.ContentType = "application/x-www-form-urlencoded;";
            req.ContentLength = bt.Length;

            using (Stream reqStream = req.GetRequestStream())//using 使用能夠釋放using段內的內存
            {
                reqStream.Write(bt, 0, bt.Length);
                reqStream.Flush();
            }
            try
            {
                using (WebResponse res = req.GetResponse())
                {
                    //在這裏對接收到的頁面內容進行處理 
                    Stream resStream = res.GetResponseStream();
                    StreamReader resStreamReader = new StreamReader(resStream, System.Text.Encoding.UTF8);
                    string resLine;
                    System.Text.StringBuilder resStringBuilder = new System.Text.StringBuilder();
                    while ((resLine = resStreamReader.ReadLine()) != null)
                    {
                        resStringBuilder.Append(resLine + System.Environment.NewLine);
                    }
                    resStream.Close();
                    resStreamReader.Close();
                    return resStringBuilder.ToString();
                }
            }
            catch (Exception ex)
            {
                return ex.Message;//url錯誤時候回報錯
            }
        }

 

 好吧,我認可我是一個不明真相的吃貨,怎麼又多了一個access_token=IsExistAccess_Token();呢,莫着急,寶寶告訴你。

當咱們閱讀文檔的時候,咱們會發現,這裏的Access_Token是每兩個小時就過時的。這裏的方法就是讓他過時的時候自動獲取。

第五坑,這裏的JSON字符串,也就是要展現的菜單,我但願你們都用小寫,若是用了大寫,那麼,呵呵,哈哈了真心的,很操蛋的,他會告訴你沒有用UTF8編碼,可是你真心是編碼過的,惋惜仍是出錯,因此,仍是小寫吧,唉

繼續說兩個小時自動獲取以後,就是經過MenuCreate(調用微信菜單接口)輸出便可。上代碼。

    /// <summary>
    /// 防止每次請求的token兩個小時的變化
    /// </summary>
    public class WA_GetAccess_Token
    {
        public WA_GetAccess_Token()
        {
        }

        public static WAEntity.Access_token GetAccess_Token()
        {
            string url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ ConfigurationManager.AppSettings["AppID"] + "&secret="+ ConfigurationManager.AppSettings["AppSecret"];
            Access_token entity = new Access_token();
            try
            {
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
                req.Method = "GET";
                using (WebResponse wr = req.GetResponse())
                {
                    HttpWebResponse myResponse = (HttpWebResponse)req.GetResponse();
                    StreamReader reader = new StreamReader(myResponse.GetResponseStream(), System.Text.Encoding.UTF8);
                    string content = reader.ReadToEnd(); 
                    Access_token token = new Access_token();
                    token = JsonHelper.ParseFromJson<Access_token>(content);
                    entity.access_token = token.access_token;
                    entity.expires_in = token.expires_in;
                }
            }
            catch{ //記錄日誌}
            return entity;
        }


        /// <summary>  
        /// 根據當前日期 判斷Access_Token 是否超期  若是超期返回新的Access_Token   不然返回以前的Access_Token  
        /// </summary>  
        /// <param name="datetime"></param>  
        /// <returns></returns>  
        public static string IsExistAccess_Token()
        {
            try
            {
                string Token = string.Empty;
                DateTime YouXRQ;
                //讀取XML文件中的數據,並顯示出來
                string filepath = HttpContext.Current.Request.MapPath("~/XMLFile.xml");
                StreamReader str = new StreamReader(filepath, System.Text.Encoding.UTF8);
                XmlDocument xml = new XmlDocument();
                xml.Load(str);
                str.Close();
                str.Dispose();
                Token = xml.SelectSingleNode("xml").SelectSingleNode("Access_Token").InnerText;
                YouXRQ = Convert.ToDateTime(xml.SelectSingleNode("xml").SelectSingleNode("Access_YouXRQ").InnerText);
                if (DateTime.Now > YouXRQ)
                {
                    DateTime _youxrq = DateTime.Now;
                    WAEntity.Access_token mode = GetAccess_Token();
                    xml.SelectSingleNode("xml").SelectSingleNode("Access_Token").InnerText = mode.access_token;
                    _youxrq = _youxrq.AddSeconds(Convert.ToInt32(mode.expires_in));
                    xml.SelectSingleNode("xml").SelectSingleNode("Access_YouXRQ").InnerText = _youxrq.ToString();
                    xml.Save(filepath);
                    Token = mode.access_token;
                }
                return Token;
            }
            catch (Exception ex)
            {
                return "";//記錄日誌
            }
        }
    }

    public class Access_token
    {
        public Access_token()
        { }

        public string access_token { get; set; }
        public string expires_in { get; set; }
    }

    public class JsonHelper
    {
        /// <summary>  
        /// 生成Json格式  
        /// </summary>  
        /// <typeparam name="T"></typeparam>  
        /// <param name="obj"></param>  
        /// <returns></returns>  
        public static string GetJson<T>(T obj)
        {
            DataContractJsonSerializer json = new DataContractJsonSerializer(obj.GetType());
            using (MemoryStream stream = new MemoryStream())
            {
                json.WriteObject(stream, obj);
                string szJson = Encoding.UTF8.GetString(stream.ToArray()); return szJson;
            }
        }
        /// <summary>  
        /// 獲取Json的Model  
        /// </summary>  
        /// <typeparam name="T"></typeparam>  
        /// <param name="szJson"></param>  
        /// <returns></returns>  
        public static T ParseFromJson<T>(string szJson)
        {
            T obj = Activator.CreateInstance<T>();
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(szJson)))
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
                return (T)serializer.ReadObject(ms);
            }
        }
    }

 原諒我又不明真相了,所謂的XMLFile.xml這又是什麼鬼,好吧,我其實不想說的這麼直白的,仍是代碼直接上比較好。

<?xml version="1.0" encoding="utf-8"?>
<xml>
  <Access_Token>獲取TOKEN</Access_Token>
  <Access_YouXRQ>2015/9/12 17:56:31</Access_YouXRQ>
</xml>

 我肯定這個你真心不想說什麼了

好吧,默默的吃着瓜子,靜靜的看着大家繼續,今天就先到這裏,隨後咱們繼續走起,已經五個坑了呀,寶寶內心苦呀。

相關文章
相關標籤/搜索