在《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调用都缩短到一行。

这么做的同时,除了让代码更加简便,我们还有两个愿望:

  1. 让API可以自动处理已经变更的AccessToken(在负载均衡等多个服务器同时操作同一个微信公众号的情况下,可能出现AccessToken在外部被刷新,导致本机AccessToken失效的情况),并且重新获取、返回最终正确的API结果。
  2. 不改变目前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

  1. Senparc.Weixin.MP SDK 微信公众平台开发教程(一):微信公众平台注册
  2. Senparc.Weixin.MP SDK 微信公众平台开发教程(二):成为开发者
  3. Senparc.Weixin.MP SDK 微信公众平台开发教程(三):微信公众平台开发验证
  4. Senparc.Weixin.MP SDK 微信公众平台开发教程(四):Hello World
  5. Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK
  6. Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler
  7. Senparc.Weixin.MP SDK 微信公众平台开发教程(七):解决用户上下文(Session)问题
  8. Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明
  9. Senparc.Weixin.MP SDK 微信公众平台开发教程(九):自定义菜单接口说明
  10. Senparc.Weixin.MP SDK 微信公众平台开发教程(十):多客服接口说明
  11. Senparc.Weixin.MP SDK 微信公众平台开发教程(十一):高级接口说明
  12. Senparc.Weixin.MP SDK 微信公众平台开发教程(十二):OAuth2.0说明
  13. Senparc.Weixin.MP SDK 微信公众平台开发教程(十三):地图相关接口说明
  14. Senparc.Weixin.MP SDK 微信公众平台开发教程(十四):请求消息去重
  15. Senparc.Weixin.MP SDK 微信公众平台开发教程(十五):消息加密
  16. Senparc.Weixin.MP SDK 微信公众平台开发教程(十六):AccessToken自动管理机制
  17. Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明
  18. Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能
  19. Senparc.Weixin.MP SDK 微信公众平台开发教程(十九):MessageHandler 的未知类型消息处理
  20. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十):使用菜单消息功能
  21. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十一):在小程序中使用 WebSocket (.NET Core)
  22. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试

Senparc.Weixin.MP SDK 微信公众平台开发教程(十六):AccessToken自动管理机制的更多相关文章

  1. Senparc.Weixin.MP SDK 微信公众平台开发教程(六):了解MessageHandler

    上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK>我们讲述了如何使用Senparc.Weixin.MP SDK ...

  2. Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能

    在Senparc.Weixin.dll v4.5.7版本开始,我们提供了Web代理功能,以方便在受限制的局域网内的应用可以顺利调用接口. 有关的修改都在Senparc.Weixin/Utilities ...

  3. Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明

    前不久微信上线了个性化菜单接口,Senparc.Weixin SDK也已经同步更新. 本次更新升级Senparc.Weixin.MP版本到v13.5.2,依赖Senparc.Weixin版本4.5.4 ...

  4. Senparc.Weixin.MP SDK 微信公众平台开发教程(三):微信公众平台开发验证

    要对接微信公众平台的"开发模式",即对接到自己的网站程序,必须在注册成功之后(见Senparc.Weixin.MP SDK 微信公众平台开发教程(一):微信公众平台注册),等待官方 ...

  5. Senparc.Weixin.MP SDK 微信公众平台开发教程(四):Hello World

    =============  以下写于2013-07-20 ============= 这一篇文章其实可以写在很前面,不过我还是希望开发者们尽多地了解清楚原理之后再下手. 通过上一篇Senparc.W ...

  6. Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK

    Senparc.Weixin.MP SDK已经涵盖了微信6.x的所有公共API. 整个项目的源代码以及已经编译好的程序集可以在这个项目中获取到:https://github.com/JeffreySu ...

  7. Senparc.Weixin.MP SDK 微信公众平台开发教程(七):解决用户上下文(Session)问题

    从这篇文章中我们已经了解了微信公众平台消息传递的方式,这种方式有一个先天的缺陷:不同用户的请求都来自同一个微信服务器,这使得常规的Session无法使用(始终面对同一个请求对象,况且还有对方服务器Co ...

  8. Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明

    一.基础说明 这里说的“通用接口(CommonAPIs)”是使用微信公众账号一系列高级功能的必备验证功能(应用于开发模式). 我们通过微信后台唯一的凭证,向通用接口发出请求,得到访问令牌(Access ...

  9. Senparc.Weixin.MP SDK 微信公众平台开发教程(九):自定义菜单接口说明

    上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明>介绍了如何通过通用接口获取AccessToken,有了AccessToken,我们就可以来操作 ...

  10. Senparc.Weixin.MP SDK 微信公众平台开发教程(十):多客服接口说明

    微信官方的多客服接口原理是通过用户发送的信息,开发者服务器返回一条指定类型的响应信息,使用户的对话状态切换到官方的多客服状态(持续一段时间),这段时间内用户发送的所有信息都不会到达开发者的服务器,而是 ...

随机推荐

  1. 把页面上的图表导出为pdf文件,分享一种请求下载文件的方法

    最近客户提出一个需求,就是把页面上的图表导出为pdf文件. 找了很多资料.终于有了点头绪.最主要是参考了HighCharts的做法.http://www.hcharts.cn/ 实现原理:把页面图表的 ...

  2. 使用.NET读取exchange邮件

    公司有个第3方的系统,不能操作源码修改错误捕获,但是错误会发一个邮件出来,里面包含了主要的信息.于是想从邮件下手,提取需要的数据 开始考虑使用的是exchange service,主要参考http:/ ...

  3. 关于echarts3地图下钻省市

    最近关于echarts3弄了个地图下钻,也许是因为运气不好,开始上来需要传递到的城市是成都,结果成都地图(js)加载下来总是挤到了一起,好尴尬,各方实验查找后发现原来是js文件中,成都市地图有个县市并 ...

  4. bash快捷建

    bash快捷建 ctrl键组合ctrl+a:光标移到行首.ctrl+b:光标左移一个字母ctrl+c:杀死当前进程.ctrl+d:退出当前 Shell.ctrl+e:光标移到行尾.ctrl+h:删除光 ...

  5. JavaScript-永远点不到的小窗口

    <!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. P1906联合权值

    描述 无向连通图 G 有 n 个点,n-1 条边.点从 1 到 n 依次编号,编号为 i 的点的权值为 WiWi, 每条边的长度均为 1.图上两点(u, v)的距离定义为 u 点到 v 点的最短距离. ...

  7. Sql Servicer 复习笔记(1) 存储过程分布

    第一步:创建表 declare @countInt int declare @age int ) begin ),@countInt), @age,'中国北京') ; ; ) begin ; end ...

  8. 如何让ECSHOP不同的分类调用不同模板方法

    如何给ecshop商品分类,显示不同的模板的话.可以通过不同的分类ID来取得不同模板.我们可以通过分类ID来判断,比如分类为1的,调用cat1.dwt.分类为2的,调用cat2.dwt,我们在cate ...

  9. sgdisk常用操作

    与fdisk创建MBR分区一样,sgdisk是一个创建GPT分区的工具,如果你还不了解GPT分区,请参考The difference between booting MBR and GPT with ...

  10. NoSQL 精粹

    1.2 阻抗失谐:关系型数据库过于简单和标准的表达方式(表+行或者说关系relation+元组tuple)不能很好的映射到应用(如表达嵌套:和其它表达方式如XML).解决办法:对象-关系映射框架,但查 ...