php微信公众账号开发之前五个坑(一)


Posted in PHP onSeptember 18, 2016

直入主题:

微信公众账号开发文档,官方版(https://mp.weixin.qq.com/wiki),相信我,我已经无力吐槽写这个文档的人了,我真心想杂碎这个键盘,但是下手之后才发现,原来键盘是我自己花钱买的。。。。尴尬了。 

废话不说,直接说怎么部署,怎么开发。 

首先,你得有一个公众平台账号,好了,开始计坑。 

第一坑,不要以为不是企业号就不能开发了,可以申请测试号的,比所谓的订阅号接口多多了。

 php微信公众账号开发之前五个坑(一)

进入后台管理之后,点击开发者工具,可以看到公众平台测试账号,直接进入即可。开始填写自己的配置。 

php微信公众账号开发之前五个坑(一)

php微信公众账号开发之前五个坑(一)

注意涂鸦部分,这部分是程序中必须要配置的东东,如果没有配置的话,这是一定不成功的。 

第二坑,当然,你这么配置也一定是不成功的,不要问我为什么。没图说个几把。。。 

php微信公众账号开发之前五个坑(一)

请不要认为企鹅帝闹着玩,这是真的,必须是80端口,其实也就发布一个域名的网站即可。因为域名的网站都是80端口出来的,继续说正题。 

企鹅帝告诉我们,要用微信账号,必须有一台服务器,然后配置我们发布的网站就行了,请注意,token是自己设定的,这个不是自动生成的,自己设定。。URL就是我们发布的网站名称 

第三坑,网站不发布,接口配置信息是永远配置不过去的,记住,是永远。 

JS接口安全域名,这个请直接参考文档(http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html)。 

JS接口安全域名的目的是为了下载图片,调用微信图片接口等等,比如当你需要调用摄像头的时候,或者说说需要上传照片的时候,这时候就需要JS安全接口了,关于具体的内容暂不做详细描述。 

在微信公众账号测试号的版本后台中,有一个体验接口权限表里面的这一项也是必须配置的。也不算是必须配置,而是这个接口可以获取到微信用户的部分信息。值得提醒的是每个公众账号对应的每个ID是唯一的,也就是说,即便网站内网不变,如果换了公众号,那么,这时候的微信公众号的数据是不能共通的,只是相对公共号唯一。 

php微信公众账号开发之前五个坑(一)

第四坑,申请微信网页授权的时候,这里的网页授权用户基本信息,这个本身没问题,但是没有提示就有问题了。 

php微信公众账号开发之前五个坑(一)

这里的网址,请注意,一定是不含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>

我确定这个你真心不想说什么了 

好吧,默默的吃着瓜子,静静的看着你们继续,今天就先到这里,随后我们继续走起,已经五个坑了呀,宝宝心里苦呀。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
在PHP中读取和写入WORD文档的代码
Apr 09 PHP
PHP开发中的错误收集,不定期更新。
Feb 03 PHP
phpmyadmin中禁止外网使用的方法
Nov 04 PHP
php绘图之加载外部图片的方法
Jan 24 PHP
php获取远程文件的内容和大小
Nov 03 PHP
在WordPress中使用PHP脚本来判断访客来自什么国家
Dec 10 PHP
php通过smtp邮件验证登陆的方法
May 11 PHP
php文件上传后端处理小技巧
May 22 PHP
php实现批量修改文件名称的方法
Jul 23 PHP
php中foreach结合curl实现多线程的方法分析
Sep 22 PHP
Yii框架 session 数据库存储操作方法示例
Nov 18 PHP
php测试kafka项目示例
Feb 06 PHP
php中实现进程锁与多进程的方法
Sep 18 #PHP
注意!PHP 7中不要做的10件事
Sep 18 #PHP
深入理解PHP类的自动载入机制
Sep 16 #PHP
PHP实现链式操作的原理详解
Sep 16 #PHP
详解PHP中websocket的使用方法
Sep 15 #PHP
PHP简单实现上一页下一页功能示例
Sep 14 #PHP
PHP自定义函数格式化json数据示例
Sep 14 #PHP
You might like
ThinkPHP提示错误Fatal error: Allowed memory size的解决方法
2015/02/12 PHP
php实现websocket实时消息推送
2018/03/30 PHP
PHP远程连接oracle数据库操作实现方法图文详解
2019/04/11 PHP
php设计模式之工厂模式用法经典实例分析
2019/09/20 PHP
jQuery中调用WebService方法小结
2011/03/28 Javascript
jQuery之自动完成组件的深入解析
2013/06/19 Javascript
jquery判断元素是否隐藏的多种方法
2014/05/06 Javascript
javascript实现无限级select联动菜单
2015/01/02 Javascript
jQuery中的通配符选择器使用总结
2016/05/30 Javascript
只需五句话搞定JavaScript作用域(经典)
2016/07/26 Javascript
canvas绘制一个常用的emoji表情
2017/03/30 Javascript
基于jQuery的表单填充实例
2017/08/22 jQuery
vue 解决form表单提交但不跳转页面的问题
2019/10/30 Javascript
使用Django的模版来配合字符串翻译工作
2015/07/27 Python
Python基于列表list实现的CRUD操作功能示例
2018/01/05 Python
cmd运行python文件时对结果进行保存的方法
2018/05/16 Python
Python实现定期检查源目录与备份目录的差异并进行备份功能示例
2019/02/27 Python
关于tensorflow的几种参数初始化方法小结
2020/01/04 Python
tensorflow dataset.shuffle、dataset.batch、dataset.repeat顺序区别详解
2020/06/03 Python
在Mac中配置Python虚拟环境过程解析
2020/06/22 Python
Jupyter Notebook添加代码自动补全功能的实现
2021/01/07 Python
印度在线购物网站:Paytmmall
2019/07/24 全球购物
IBatis持久层技术
2016/07/18 面试题
销售找工作求职信
2013/12/20 职场文书
城市轨道交通工程职业规划书范文
2014/01/18 职场文书
2014年道德讲堂实施方案
2014/03/05 职场文书
大学生评语大全
2014/04/18 职场文书
党的群众路线教育实践活动对照检查材料思想汇报
2014/09/19 职场文书
迁户口计划生育证明
2014/10/19 职场文书
2014年教师教学工作总结
2014/11/08 职场文书
2014年小学教导处工作总结
2014/12/19 职场文书
PHP 对接美团大众点评团购券(门票)的开发步骤
2021/04/03 PHP
react antd实现动态增减表单
2021/06/03 Javascript
Python制作动态字符画的源码
2021/08/04 Python
Django + Taro 前后端分离项目实现企业微信登录功能
2022/04/07 Python
Linux中如何安装并部署Redis
2022/04/18 Servers