博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Senparc.Weixin.MP SDK 微信公众平台开发教程(十六):AccessToken自动管理机制
阅读量:6073 次
发布时间:2019-06-20

本文共 6651 字,大约阅读时间需要 22 分钟。

    在《》中,我介绍了获取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{    ///     /// 针对AccessToken无效或过期的自动处理类    ///     public static class ApiHandlerWapper    {        ///         /// 使用AccessToken进行操作时,如果遇到AccessToken错误的情况,重新获取AccessToken一次,并重试。        /// 使用此方法之前必须使用AccessTokenContainer.Register(_appId, _appSecret);或JsApiTicketContainer.Register(_appId, _appSecret);方法对账号信息进行过注册,否则会出错。        ///         /// 
/// /// AccessToken或AppId。如果为null,则自动取已经注册的第一个appId/appSecret来信息获取AccessToken。 /// 请保留默认值true,不用输入。 ///
public static T TryCommonApi
(Func
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的源代码原来是这样的:

///         /// 获取当前菜单,如果菜单不存在,将返回null        ///         ///         /// 
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
(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()方法之后:

///         /// 获取当前菜单,如果菜单不存在,将返回null        ///         /// AccessToken或AppId。当为AppId时,如果AccessToken错误将自动获取一次。当为null时,获取当前注册的第一个AppId。        /// 
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
(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://xingx.baihongyu.com/

你可能感兴趣的文章
用PL/SQL Developer导出表数据的时候,窗口一闪而过解决办法
查看>>
连接排查过程
查看>>
26.CSS前缀和rem
查看>>
java transient关键字
查看>>
mvc model 传值两种方式区别
查看>>
spring
查看>>
正方教务处抓包分析
查看>>
第一次作业
查看>>
openjudge2985(数字组合)
查看>>
步步为营 .NET 设计模式学习笔记 二十二、Memento(备望录模式)
查看>>
步步为营UML建模系列四、状态图(State)
查看>>
(7)javascript的程序控制结构及语句------(2)循环控制语句、跳转语句、对话框...
查看>>
asp.net上传图片
查看>>
如何修改EF的代码生成策略
查看>>
Yii2.0实现语言包切换功能
查看>>
寒假的Java学习笔记总结1
查看>>
C#判断操作系统的位数
查看>>
利用a标签自动解析URL
查看>>
堆,栈,字符串池,以及进程,线程浅谈内存(个人理解)
查看>>
sql语句(Mysql数据库)
查看>>