Senparc.Weixin.MP SDK 微信公众平台开发教程(十六):AccessToken自动管理机制
在《Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明》中,我介绍了获取AccessToken(通用接口)的方法。
在实际的开发过程中,所有的高级接口都需要提供AccessToken,因此我们每次在调用高级接口之前,都需要执行一次获取AccessToken的方法,例如:
var accessToken = AccessTokenContainer.TryGetAccessToken(appId, appSecret);
或者当你对appId和appSecret进行过全局注册之后,也可以这样做:
var accessToken = AccessTokenContainer.GetAccessToken(_appId);
然后使用这个accessToken输入到高级接口的方法中,例如我们可以这样获取菜单:
var result = CommonApi.GetMenu(accessToken);
通常情况下,这已经是一个很简洁的API调用过程。但是我们不愿意就这样停止,我们准备把几乎所有的API调用都缩短到一行。
这么做的同时,除了让代码更加简便,我们还有两个愿望:
- 让API可以自动处理已经变更的AccessToken(在负载均衡等多个服务器同时操作同一个微信公众号的情况下,可能出现AccessToken在外部被刷新,导致本机AccessToken失效的情况),并且重新获取、返回最终正确的API结果。
- 不改变目前API调用的方式,完全向下兼容。
调用代码
修改之后,我们可以直接这样一行调用API,每次只需要提供一个appId:
var result = CommonApi.GetMenu(appId);
当前在执行之前,我们需要像以前一样全局注册一下appId和appSecret:
AccessTokenContainer.Register(_appId, _appSecret);//全局只需注册一次,例如可以放在Global的Application_Start()方法中。
可以看到,原先的accessToken换成了appId(新版本仍然支持输入accessToken),省去了获取accessToken的过程。具体的过程见下文说明。
SDK源代码实现过程
之前为了实现自动处理(预料外的)过期的AccessToken,SDK已经提供了Senparc.Weixin.MP/AccessTokenHandlerWapper.Do()方法。这次升级将AccessTokenHandlerWapper.cs重命名为ApiHandlerWapper.cs,废除Do()方法,添加TryCommonApi()方法,代码如下:
namespace Senparc.Weixin.MP
{
/// <summary>
/// 针对AccessToken无效或过期的自动处理类
/// </summary>
public static class ApiHandlerWapper
{
/// <summary>
/// 使用AccessToken进行操作时,如果遇到AccessToken错误的情况,重新获取AccessToken一次,并重试。
/// 使用此方法之前必须使用AccessTokenContainer.Register(_appId, _appSecret);或JsApiTicketContainer.Register(_appId, _appSecret);方法对账号信息进行过注册,否则会出错。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fun"></param>
/// <param name="accessTokenOrAppId">AccessToken或AppId。如果为null,则自动取已经注册的第一个appId/appSecret来信息获取AccessToken。</param>
/// <param name="retryIfFaild">请保留默认值true,不用输入。</param>
/// <returns></returns>
public static T TryCommonApi<T>(Func<string, T> fun, string accessTokenOrAppId = null, bool retryIfFaild = true) where T : WxJsonResult
{
string appId = null;
string accessToken = null; if (accessTokenOrAppId == null)
{
appId = AccessTokenContainer.GetFirstOrDefaultAppId();
if (appId == null)
{
throw new WeixinException("尚无已经注册的AppId,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!");
}
}
else if (ApiUtility.IsAppId(accessTokenOrAppId))
{
if (!AccessTokenContainer.CheckRegistered(accessTokenOrAppId))
{
throw new WeixinException("此appId尚未注册,请先使用AccessTokenContainer.Register完成注册(全局执行一次即可)!");
} appId = accessTokenOrAppId;
}
else
{
//accessToken
accessToken = accessTokenOrAppId;
} T result = null; try
{
if (accessToken == null)
{
var accessTokenResult = AccessTokenContainer.GetAccessTokenResult(appId, false);
accessToken = accessTokenResult.access_token;
}
result = fun(accessToken);
}
catch (ErrorJsonResultException ex)
{
if (!retryIfFaild
&& appId != null
&& ex.JsonResult.errcode == ReturnCode.获取access_token时AppSecret错误或者access_token无效)
{
//尝试重新验证
var accessTokenResult = AccessTokenContainer.GetAccessTokenResult(appId, true);
accessToken = accessTokenResult.access_token;
result = TryCommonApi(fun, appId, false);
}
}
return result;
}
}
}
对应API的源代码原来是这样的:
/// <summary>
/// 获取当前菜单,如果菜单不存在,将返回null
/// </summary>
/// <param name="accessToken"></param>
/// <returns></returns>
public static GetMenuResult GetMenu(string accessToken)
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken); var jsonString = HttpUtility.RequestUtility.HttpGet(url, Encoding.UTF8);
//var finalResult = GetMenuFromJson(jsonString); GetMenuResult finalResult;
JavaScriptSerializer js = new JavaScriptSerializer();
try
{
var jsonResult = js.Deserialize<GetMenuResultFull>(jsonString);
if (jsonResult.menu == null || jsonResult.menu.button.Count == 0)
{
throw new WeixinException(jsonResult.errmsg);
} finalResult = GetMenuFromJsonResult(jsonResult);
}
catch (WeixinException ex)
{
finalResult = null;
} return finalResult;
}
现在使用TryCommonApi()方法之后:
/// <summary>
/// 获取当前菜单,如果菜单不存在,将返回null
/// </summary>
/// <param name="accessTokenOrAppId">AccessToken或AppId。当为AppId时,如果AccessToken错误将自动获取一次。当为null时,获取当前注册的第一个AppId。</param>
/// <returns></returns>
public static GetMenuResult GetMenu(string accessTokenOrAppId)
{
return ApiHandlerWapper.TryCommonApi(accessToken =>
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken); var jsonString = HttpUtility.RequestUtility.HttpGet(url, Encoding.UTF8);
//var finalResult = GetMenuFromJson(jsonString); GetMenuResult finalResult;
JavaScriptSerializer js = new JavaScriptSerializer();
try
{
var jsonResult = js.Deserialize<GetMenuResultFull>(jsonString);
if (jsonResult.menu == null || jsonResult.menu.button.Count == 0)
{
throw new WeixinException(jsonResult.errmsg);
} finalResult = GetMenuFromJsonResult(jsonResult);
}
catch (WeixinException ex)
{
finalResult = null;
} return finalResult;
}, accessTokenOrAppId);
}
我们可以观察到有这样几处变化:
1. 原先的accessToken变量名称改为了accessTokenOrAppId(新版本中所有相关接口都将如此变化)。
修改之后,这个参数可以输入accessToken(向下兼容),也可以输入appId(无需再获取accessToken),SDK会根据字符串长度自动判断属于哪种类型的参数。提供的参数有3种可能:
a) appId。使用appId需要事先对appId和appSecret进行全局注册(上文已说过),当调用API的过程中发现缓存的AccessToken过期时,SDK会自动刷新AccessToken,并重新尝试一次API请求,确保返回正确的结果。如果appId没有被注册过,会抛出异常。
b) accessToken。这种情况下将使用原始的请求方式,如果accessToken无效,将直接抛出异常,不会重试。
c) null。当accessTokenOrAppId参数为null时,SDK会自动获取全局注册的第一个appId。如果某个应用只针对一个确定的微信号开发,可以使用这种方法。当全局没有注册任何appId时,将抛出异常。
2. 原方法内的访问API的代码没有做任何修改,只是被嵌套到了return ApiHandlerWapper.TryCommonApi(accessToken =>{...},accessTokenOrAppId)的方法中,以委托的形式出现,目的是为了在第一次可能的请求失败之后,SDK可以自动执行一次一模一样的代码。
此功能已经在Senparc.Weixin.MP v12.1中发布。
系列教程索引
地址:http://www.cnblogs.com/szw/archive/2013/05/14/weixin-course-index.html
- Senparc.Weixin.MP SDK 微信公众平台开发教程(一):微信公众平台注册
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二):成为开发者
- Senparc.Weixin.MP SDK 微信公众平台开发教程(三):微信公众平台开发验证
- Senparc.Weixin.MP SDK 微信公众平台开发教程(四):Hello World
- Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK
- Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler
- Senparc.Weixin.MP SDK 微信公众平台开发教程(七):解决用户上下文(Session)问题
- Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明
- Senparc.Weixin.MP SDK 微信公众平台开发教程(九):自定义菜单接口说明
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十):多客服接口说明
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十一):高级接口说明
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十二):OAuth2.0说明
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十三):地图相关接口说明
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十四):请求消息去重
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十五):消息加密
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十六):AccessToken自动管理机制
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十九):MessageHandler 的未知类型消息处理
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二十):使用菜单消息功能
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二十一):在小程序中使用 WebSocket (.NET Core)
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试
Senparc.Weixin.MP SDK 微信公众平台开发教程(十六):AccessToken自动管理机制的更多相关文章
- Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler
上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK>我们讲述了如何使用Senparc.Weixin.MP SDK ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能
在Senparc.Weixin.dll v4.5.7版本开始,我们提供了Web代理功能,以方便在受限制的局域网内的应用可以顺利调用接口. 有关的修改都在Senparc.Weixin/Utilities ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明
前不久微信上线了个性化菜单接口,Senparc.Weixin SDK也已经同步更新. 本次更新升级Senparc.Weixin.MP版本到v13.5.2,依赖Senparc.Weixin版本4.5.4 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(三):微信公众平台开发验证
要对接微信公众平台的"开发模式",即对接到自己的网站程序,必须在注册成功之后(见Senparc.Weixin.MP SDK 微信公众平台开发教程(一):微信公众平台注册),等待官方 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(四):Hello World
============= 以下写于2013-07-20 ============= 这一篇文章其实可以写在很前面,不过我还是希望开发者们尽多地了解清楚原理之后再下手. 通过上一篇Senparc.W ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK
Senparc.Weixin.MP SDK已经涵盖了微信6.x的所有公共API. 整个项目的源代码以及已经编译好的程序集可以在这个项目中获取到:https://github.com/JeffreySu ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(七):解决用户上下文(Session)问题
从这篇文章中我们已经了解了微信公众平台消息传递的方式,这种方式有一个先天的缺陷:不同用户的请求都来自同一个微信服务器,这使得常规的Session无法使用(始终面对同一个请求对象,况且还有对方服务器Co ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明
一.基础说明 这里说的“通用接口(CommonAPIs)”是使用微信公众账号一系列高级功能的必备验证功能(应用于开发模式). 我们通过微信后台唯一的凭证,向通用接口发出请求,得到访问令牌(Access ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(九):自定义菜单接口说明
上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明>介绍了如何通过通用接口获取AccessToken,有了AccessToken,我们就可以来操作 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十):多客服接口说明
微信官方的多客服接口原理是通过用户发送的信息,开发者服务器返回一条指定类型的响应信息,使用户的对话状态切换到官方的多客服状态(持续一段时间),这段时间内用户发送的所有信息都不会到达开发者的服务器,而是 ...
随机推荐
- OD使用教程12
载入程序输入关键字: 双击进入程序 仔细看发现并没有跳转直接跳到这个mov,往上看发现retn上面有一个push,在这种编写手法当中这种组合相当于一个jmp, 跳到离它最近的一个值(在这就是004A5 ...
- iOS开发中的各种错误
提交iTunesconnect遇到的问题: 1. error itms-90179 Invalid Code Signing. 解决:发现是发布正式被撤销了,重新生成发布Certificates,重新 ...
- windows下nginx的启动关闭
Windows下Nginx的启动.停止等命令 在Windows下使用Nginx,我们需要掌握一些基本的操作命令,比如:启动.停止Nginx服务,重新载入Nginx等,下面我就进行一些简单的介绍. .启 ...
- noip2012-day2-t2
[问题描述] 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们 ...
- spring的beans.xml中classpath
classpath就是代表 /WEB-INF /classes/ 这个路径(如果不理解该路径,就把一个web工程发布为war包,然后用winrar查看其包内路径就理解啦) 常用的场景: 在SSH架构中 ...
- 如何防止JAVA反射对单例类的攻击?
在我的上篇随笔中,我们知道了创建单例类有以下几种方式: (1).饿汉式; (2).懒汉式(.加同步锁的懒汉式.加双重校验锁的懒汉式.防止指令重排优化的懒汉式); (3).登记式单例模式; (4).静态 ...
- 电子科技大学第八届ACM趣味程序设计竞赛第四场(正式赛)题解
A. Picking&Dancing 有一列n个石子,两人交替取石子,每次只能取连续的两个,取走后,剩下的石子仍然排成1列.问最后剩下的石子数量是奇数还是偶数. 读懂题意就没什么好说的. #i ...
- MATLAB 画出三个通信小区cell边界示意图
d=1000; %两个小区中心间距离的一半 rcell=2*d/sqrt(3); %小区半径 ncell=3; %小区个数 cellposition=zeros(ncell,2); %初始化小区中心位 ...
- Android图片复制
public void saveImage2Phone(SlideShowImage image){ String imagePath; if(Environment.getExternalStora ...
- 项目文件""已被重命名或已不在解决方案中
如题,这个错误在vs2010里,开始编译的时候出现了,我的解决办法就是把所有的project都移除,然后再添加进来,就不弹这个错误了.