这里有新鲜出炉的精品教程,程序狗速度看过来!
微信(英文名:wechat)是腾讯公司于2011年1月21日推出的一个为智能终端提供即时通讯服务的免费应用程序,微信支持跨通信运营商、跨操作系统平台通过网络快速发送免费语音短信、视频、图片和文字,同时,也可以使用通过共享流媒体内容的资料和基于位置的社交插件“摇一摇”、“漂流瓶”、“朋友圈”、”公众平台“、”语音记事本“等服务插件。
这篇文章主要为大家详细介绍了php微信公众账号开发之前五个坑,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
直入主题:
微信公众账号开发文档,官方版(https://mp.weixin.qq.com/wiki),相信我,我已经无力吐槽写这个文档的人了,我真心想杂碎这个键盘,但是下手之后才发现,原来键盘是我自己花钱买的。。。。尴尬了。
废话不说,直接说怎么部署,怎么开发。
首先,你得有一个公众平台账号,好了,开始计坑。
第一坑,不要以为不是企业号就不能开发了,可以申请测试号的,比所谓的订阅号接口多多了。
进入后台管理之后,点击开发者工具,可以看到公众平台测试账号,直接进入即可。开始填写自己的配置。
注意涂鸦部分,这部分是程序中必须要配置的东东,如果没有配置的话,这是一定不成功的。
第二坑,当然,你这么配置也一定是不成功的,不要问我为什么。没图说个几把。。。
请不要认为企鹅帝闹着玩,这是真的,必须是80端口,其实也就发布一个域名的网站即可。因为域名的网站都是80端口出来的,继续说正题。
企鹅帝告诉我们,要用微信账号,必须有一台服务器,然后配置我们发布的网站就行了,请注意,token是自己设定的,这个不是自动生成的,自己设定。。URL就是我们发布的网站名称
第三坑,网站不发布,接口配置信息是永远配置不过去的,记住,是永远。
JS接口安全域名,这个请直接参考文档(http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html)。
JS接口安全域名的目的是为了下载图片,调用微信图片接口等等,比如当你需要调用摄像头的时候,或者说说需要上传照片的时候,这时候就需要JS安全接口了,关于具体的内容暂不做详细描述。
在微信公众账号测试号的版本后台中,有一个体验接口权限表里面的这一项也是必须配置的。也不算是必须配置,而是这个接口可以获取到微信用户的部分信息。值得提醒的是每个公众账号对应的每个ID是唯一的,也就是说,即便网站内网不变,如果换了公众号,那么,这时候的微信公众号的数据是不能共通的,只是相对公共号唯一。
第四坑,申请微信网页授权的时候,这里的网页授权用户基本信息,这个本身没问题,但是没有提示就有问题了。
这里的网址,请注意,一定是不含www的,而且后面没有反斜杠,也就是说这里的网址的回调格式是 abc.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()
- <em id="__mceDel"> {
- string weixin1 = "";
- weixin1 = @" {
- ""button"":[
- {
- ""type"":""click"",
- ""name"":""你好!"",
- ""key"":""hello""
- },
- {
- ""type"":""view"",
- ""name"":""公司简介"",
- ""url"":""http://www.xnfhtech.com""
- },
- {
- ""name"":""产品介绍"",
- ""sub_button"":[
- {
- ""type"":""click"",
- ""name"":""产品1"",
- ""key"":""p1""
- },
- {
- ""type"":""click"",
- ""name"":""产品2"",
- ""key"":""p2""
- }]
- }] }";
- string access_token = Tools.WA_GetAccess_Token.IsExistAccess_Token();
- string i = this.MenuCreate(menu, access_token);
- Response.Write(i);
- }<br><br>
- </em>
- 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>
我确定这个你真心不想说什么了
好吧,默默的吃着瓜子,静静的看着你们继续,今天就先到这里,随后我们继续走起,已经五个坑了呀,宝宝心里苦呀。
来源: http://www.phperz.com/article/17/0829/342696.html