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 相关文章推荐
我的论坛源代码(七)
Oct 09 PHP
smarty模板嵌套之include与fetch性能测试
Dec 05 PHP
如何让搜索引擎抓取AJAX内容解决方案
Aug 25 PHP
thinkphp获取栏目和文章当前位置的方法
Oct 29 PHP
php无限极分类递归排序实现方法
Nov 11 PHP
PHP连接操作access数据库实例
Mar 30 PHP
php用户注册信息验证正则表达式
Nov 12 PHP
在WordPress中使用PHP脚本来判断访客来自什么国家
Dec 10 PHP
基于ThinkPHP实现批量删除
Dec 18 PHP
Yii框架表单模型和验证用法
May 20 PHP
ucenter中词语过滤原理分析
Jul 13 PHP
Laravel5.5 手动分页和自定义分页样式的简单实现
Oct 15 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
PHP中call_user_func_array()函数的用法演示
2012/02/05 PHP
如何解决phpmyadmin导入数据库文件最大限制2048KB
2015/10/09 PHP
PHP简单获取随机数的常用方法小结
2017/06/07 PHP
Laravel框架自定义公共函数的引入操作示例
2019/04/16 PHP
Extjs学习笔记之一 初识Extjs之MessageBox
2010/01/07 Javascript
javascript showModalDialog 内跳转页面的问题
2010/11/25 Javascript
JavaScript的document对象和window对象详解
2010/12/30 Javascript
面向对象的Javascript之二(接口实现介绍)
2012/01/27 Javascript
javascript实现依次输入input自动定焦
2014/12/23 Javascript
JS+CSS实现简易的滑动门效果代码
2015/09/24 Javascript
javascript获取网页各种高宽及位置的方法总结
2016/07/27 Javascript
javascript 动态脚本添加的简单方法
2016/10/11 Javascript
Bootstrap 过渡效果Transition 模态框(Modal)
2017/03/17 Javascript
详解Webstorm 新建.vue文件支持高亮vue语法和es6语法
2017/10/26 Javascript
javascript异步编程的六种方式总结
2019/05/17 Javascript
Vue scoped及deep使用方法解析
2020/08/01 Javascript
AngularJs的$http发送POST请求,php无法接收Post的数据问题及解决方案
2020/08/13 Javascript
python Django模板的使用方法(图文)
2013/11/04 Python
基于Python 的进程管理工具supervisor使用指南
2016/09/18 Python
python3 图片referer防盗链的实现方法
2018/03/12 Python
Python requests模块实例用法
2019/02/11 Python
Python判断字符串是否xx开始或结尾的示例
2019/08/08 Python
python列表推导式入门学习解析
2019/12/02 Python
基于SpringBoot构造器注入循环依赖及解决方式
2020/04/26 Python
matplotlib 多个图像共用一个colorbar的实现示例
2020/09/10 Python
PyTorch中clone()、detach()及相关扩展详解
2020/12/09 Python
英国皇室御用百货:福南梅森(Fortnum & Mason)
2017/12/03 全球购物
C语言开发工程师测试题
2016/12/20 面试题
成人毕业生自我鉴定
2013/10/18 职场文书
承诺书的签字人,需不需要承担相应的责任?
2019/07/09 职场文书
Python利用folium实现地图可视化
2021/05/23 Python
详解Redis复制原理
2021/06/04 Redis
DE1103使用报告
2022/04/05 无线电
MySql重置root密码 --skip-grant-tables
2022/04/11 MySQL
Python 使用 Frame tkraise() 方法在 Tkinter 应用程序中的Frame之间切换
2022/04/24 Python
python playwright之元素定位示例详解
2022/07/23 Python