微信的access_token每次获取有效期为7200秒,也就是两个小时,在这两个小时内,再重复获取access_token不仅浪费带宽和速度,也增加了出错的几率,而且由于微信对于每日获取次数做了限制,所以每次操作都要获取一遍肯定不是一个好的办法,于是需要我们将获取到的access_token保存在本地,有效期内使用我们本地的数据。
由于我的场景是多用户,所以将AppId、Secret保存下来定时获取的方式肯定不合适。而由于没有太频繁的调用,所以我暂时选择使用json文件将用户的id、access_token、获取时间保存在本地json文件内,正好数据上也是键值对的组合,如果调用频繁那么可以试试键值对的数据库,比如redis之类的。
流程图:
【待加】
操作json我比较习惯使用json.net的库,即Newtonsoft.Json,引用之后便可以使用了。而json.net的网站文档还是非常简单不健全的,我搜了很多中文的个人博客,总觉得很混淆,概念理不清,直到我打开了stackoverflow上这个问题的页面,才终于理清了思路,之后再看官方文档就简单多了。
A JToken is a generic representation of a JSON value of any kind. It could be a string, object, array, property, etc.
A JProperty is a single JToken value paired with a name. It can only be added to a JObject, and its value cannot be another JProperty.
A JObject is a collection of JProperties. It cannot hold any other kind of JToken directly.
好了,以下是具体代码:
- 函数
System.AppDomain.CurrentDomain.BaseDirectory曾经突然抽了,得到的路径没有反斜杠,过了个周末又莫名其妙的好了,具体原因不明;
File.Create(mem_access_token)后面一定要跟Close(),否则文件没有释放,其他进程无法操作
- 初始化Jobject对象不能用字符串"{}"转换,而是直接
JObject AllAccToken = new JObject();
string access_token = "";
JObject AllAccToken = new JObject();
string mem_access_token = System.AppDomain.CurrentDomain.BaseDirectory + @"\Resource\js\MemAccessToken.json";
if(!File.Exists(mem_access_token))
{
File.Create(mem_access_token).Close();
using(StreamWriter sw = new StreamWriter(mem_access_token, false))
{
sw.Write(AllAccToken);
}
}
using (FileStream fs = new FileStream(mem_access_token, FileMode.Open))
{
using (StreamReader sr = new StreamReader(fs))
{
all_access_token = sr.ReadToEnd();
}
}
查询key
如果存在,则查询对应的token是否已经过期,如果没过期则取值,否则调用微信接口获取并修改保存文件;如果不存在直接调用微信接口获取并添加保存文件
调用的方法getAccessTokenFromWeiXin如果有access_token则返回access_token,否则返回errcode,因为errcode是5位,所以判断时长度写的6
if(AllAccToken[key] != null)
{
DateTime token_time = DateTime.Parse((string)AllAccToken.SelectToken(key).SelectToken("token_time"));
//token未过期,读取
if (token_time.AddSeconds(7200) > DateTime.Now)
{
return (string)AllAccToken.SelectToken(key).SelectToken("access_token");
}
//token已过期,重新获取并保存
else
{
access_token = getAccessTokenFromWeiXin(Data);
if ((access_token + "").Length > 6)
{
AllAccToken[key]["access_token"] = access_token;
AllAccToken[key]["token_time"] = DateTime.Now;
using (StreamWriter sw = new StreamWriter(mem_access_token, false))
{
sw.Write(AllAccToken.ToString());
}
}
}
}
//文件中没有token,获取并插入文件
else
{
access_token = getAccessTokenFromWeiXin(Data);
if((access_token + "").Length > 6)
{
string new_access_token = @"{""access_token"":""" + access_token + @""",""token_time"":""" + DateTime.Now.ToString() + @"""}";
AllAccToken.Add(memberPKId.ToString(), JToken.Parse(new_access_token));
using (StreamWriter sw = new StreamWriter(mem_access_token, false))
{
sw.Write(AllAccToken.ToString());
}
}
}
另外两个方法代码
调用微信接口获取access_token
public static string getAccessTokenFromWeiXin(ShpWeiXinShopAppData Data)
{
Uri uri = new Uri(String.Format(@"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", Data.WxAppId_NULL, Data.WxSecret_NULL));
HttpWebRequest wxReq = HttpWebRequest.Create(uri) as HttpWebRequest;
string wxJson = "";
using (HttpWebResponse wxTokenRes = wxReq.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(wxTokenRes.GetResponseStream());
wxJson = reader.ReadToEnd();
}
JObject jo = JObject.Parse(wxJson);
string access_token = jo["access_token"] == null ? (string)jo["errcode"] : (string)jo["access_token"];
return access_token;
}
根据微信错误号获取微信错误信息
微信的所有返回码我也保存成了json文件,方便用户提示,而且这个文件可以做成只读的。
public static string WeiXinError(string key)
{
using (FileStream fs = new FileStream(System.AppDomain.CurrentDomain.BaseDirectory + @"\Resource\XML\WeiXinErrcode.json", FileMode.Open))
{
using(StreamReader sr = new StreamReader(fs))
{
string error = sr.ReadToEnd();
JObject wxErr = JObject.Parse(error);
return wxErr[key].ToString();
}
}
}
小结
经过这次改进,程序优化不少,而且明确了改进方向;
json操作方法很多,找到自己习惯的就好,而这种键值对的方式其实还是很有用的,如果操作频繁的话可以考虑读入内存中操作、数据库中操作等方式。