Senparc.Weixin.MP SDK 微信公众平台开发教程(十六):AccessToken自动管理机制

简介: 在《Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明》中,我介绍了获取AccessToken(通用接口)的方法。     在实际的开发过程中,所有的高级接口都需要提供AccessToken,因此我们每次在调用高级接口之前,都需要执行一次获取AccessT...

    在《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代理功能

 

QQ:498977166

http://szw.cnblogs.com/
研究、探讨.NET开发
转载请注明出处和作者,谢谢!

 

微信开发深度解析:微信公众号、小程序高效开发秘籍
Senparc官方教程《微信开发深度解析:微信公众号、小程序高效开发秘籍》,耗时2年精心打造的微信开发权威教程,点击这里,购买正版

 

目录
相关文章
|
11天前
|
小程序 安全 数据安全/隐私保护
微信小程序全栈开发中的身份认证与授权机制
【4月更文挑战第12天】本文探讨了微信小程序全栈开发中的身份认证与授权机制。身份认证包括手机号验证、微信登录和第三方登录,而授权机制涉及角色权限控制、ACL和OAuth 2.0。实践中,开发者可利用微信登录获取用户信息,集成第三方登录,以及实施角色和ACL进行权限控制。注意点包括安全性、用户体验和合规性,以保障小程序的安全运行和良好体验。通过这些方法,开发者能有效掌握小程序全栈开发技术。
|
开发工具 Android开发 Windows
Android sdk下载安装配置教程
Android sdk下载安装配置教程
Android sdk下载安装配置教程
|
1月前
|
JSON 小程序 JavaScript
【微信小程序】-- 宿主环境 & 通信模型 & 运行机制介绍(五)
【微信小程序】-- 宿主环境 & 通信模型 & 运行机制介绍(五)
|
4月前
|
小程序 安全 JavaScript
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
|
7月前
|
Web App开发 前端开发 JavaScript
SAP UI5 应用开发教程之一百 - 如何修改 SAP UI5 框架的源代码实现,以及使用本地部署的 SAP UI5 SDK 试读版
SAP UI5 应用开发教程之一百 - 如何修改 SAP UI5 框架的源代码实现,以及使用本地部署的 SAP UI5 SDK 试读版
64 0
|
安全 JavaScript API
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
191 0
.NET微信网页开发之通过UnionID机制,解决用户在不同公众号,或在公众号、移动应用之间帐号统一问题
|
Java API 开发工具
安卓集成云端java sdk教程
安卓项目中,集成云端java sdk,完成API调用。
444 2
安卓集成云端java sdk教程
|
数据库 云计算
微信公众平台完整开发教程 by ZTalk
微信已然成了一种生活方式:群聊、语音、视频,照片/视频/位置共享,支付和商店有了,还能通过公众账号来获取各种资源……如今的微信就是在日中天的状态(最新估值已达 600 亿),公众平台带来的影响有目共睹,所以很多人都有自己的公众号,想充分利用却无处下手,所以我们在这里给大家提供一下微信公众平台的开发教程,不谢自拿。
146 0
微信公众平台完整开发教程 by ZTalk
|
消息中间件 存储 缓存
【微服务】微信的消息收发机制
关注公众号“达摩院首座”,了解开发者最真实生活
1067 0
【微服务】微信的消息收发机制
|
JSON Java API
阿里云SDK核心库(Core SDK)使用教程
本文档主要介绍:JAVA、Python、PHP、JavaScript、NET、Go六款开发语言Core SDK的安装及使用。
6820 0
阿里云SDK核心库(Core SDK)使用教程