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

时间:2022-09-17 10:15:53

直入主题:

微信公众账号开发文档,官方版(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的情况下,检测返回值即可。直接上代码 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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()   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<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>
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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(调用微信菜单接口)输出即可。上代码。 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/// <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这又是什么鬼,好吧,我其实不想说的这么直白的,还是代码直接上比较好。

?
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<xml>
 <access_token>获取token</access_token>
 <access_youxrq>2015/9/12 17:56:31</access_youxrq>
</xml>

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。