在《微信大眾平台開發:通用介面說明》中,我介紹了取得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中發布。
更多微信公眾平台開發:AccessToken自動管理機制相關文章請關注PHP中文網!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

Dreamweaver CS6
視覺化網頁開發工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。