在《微信大眾平臺開發(fā):通用介面說明》中,我介紹了取得AccessToken(通用介面)的方法。
在實際的開發(fā)過程中,所有的高階介面都需要提供AccessToken,因此我們每次在呼叫高階介面之前,都需要執(zhí)行一次取得AccessToken的方法,例如:
var accessToken = AccessTokenContainer.TryGetAccessToken(appId, appSecret);
或者當你對appId和appSecret進行過全域註冊之後,也可以這樣做:
var accessToken = AccessTokenContainer.GetAccessToken(_appId);
然後使用這個accessToken輸入到高級介面的方法中,例如我們可以這樣取得選單:
var result = CommonApi.GetMenu(accessToken);
通常情況下,這已經(jīng)是一個很簡潔的API呼叫過程。但是我們不願意就這樣停止,我們準備把幾乎所有的API呼叫都縮短到一行。
? ? ?這麼做的同時,除了讓程式碼更簡便,我們還有兩個願望:
讓API可以自動處理已經(jīng)變更的AccessToken(在負載平衡等多個伺服器同時操作同在一個微信公眾號的情況下,可能出現(xiàn)AccessToken在外部被刷新,導致本機AccessToken失效的情況),並且重新取得、傳回最終正確的API結(jié)果。
不改變目前API呼叫的方式,完全向下相容。
?
呼叫程式碼
修改之後,我們可以直接這樣一行呼叫API,每次只需要提供一個appId:
var result = CommonApi.GetMenu(appId);
目前在執(zhí)行之前,我們需要像以前一樣全域註冊appId和appSecret:
AccessTokenContainer.Register(_appId, _appSecret);//全局只需注冊一次,例如可以放在Global的Application_Start()方法中。
可以看到,原先的accessToken換成了appId(新版本仍然支援輸入accessToken),省去了獲取accessToken的過程。具體的過程請見下文說明。
SDK原始碼實作過程
? ? ?之前為了實現(xiàn)自動處理(預料外的)過期的AccessToken,SDK已經(jīng)提供了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,則自動取已經(jīng)注冊的第一個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("尚無已經(jīng)注冊的AppId,請先使用AccessTokenContainer.Register完成注冊(全局執(zhí)行一次即可)!"); } } else if (ApiUtility.IsAppId(accessTokenOrAppId)) { if (!AccessTokenContainer.CheckRegistered(accessTokenOrAppId)) { throw new WeixinException("此appId尚未注冊,請先使用AccessTokenContainer.Register完成注冊(全局執(zhí)行一次即可)!"); } 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; }
現(xiàn)在使用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變數(shù)名稱改為accessTokenOrAppId(新版本中所有相關(guān)接口都將如此變化)。
? ? 修改之後,這個參數(shù)可以輸入accessToken(向下相容),也可以輸入appId(無需再取得accessToken),SDK會根據(jù)字串長度自動判斷屬於哪種類型的參數(shù)。提供的參數(shù)有3種可能:
? ? ? ? a)?appId。使用appId需要事先對appId和appSecret進行全域註冊(上文已說過),當呼叫API的過程中發(fā)現(xiàn)快取的AccessToken過期時,SDK會自動刷新AccessToken,並重新嘗試一次API請求,確保傳回正確的結(jié)果。如果appId沒有被註冊過,會拋出例外。
? ? ? ? b)?accessToken。這種情況將使用原始的請求方式,如果accessToken無效,將直接拋出異常,不會重試。
? ? ? ? c)?null。當accessTokenOrAppId參數(shù)為null時,SDK會自動取得全域註冊的第一個appId。如果某個應用程式只針對一個確定的微訊號開發(fā),可以使用這種方法。當全域沒有註冊任何appId時,將會拋出例外。
? ? 2. 原方法內(nèi)的存取API的程式碼沒有做任何修改,只是被嵌套到了return ApiHandlerWapper.TryCommonApi(accessToken =>{...},accessTokenOrAppId)的方法中,以委託的形式出現(xiàn),目的是為了在第一次可能的請求失敗之後,SDK可以自動執(zhí)行一次一模一樣的程式碼。
?
? ? 此功能已在Senparc.Weixin.MP v12.1中發(fā)布。
更多微信公眾平臺開發(fā):AccessToken自動管理機制相關(guān)文章請關(guān)注PHP中文網(wǎng)!

熱AI工具

Undress AI Tool
免費脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動的應用程序,用於創(chuàng)建逼真的裸體照片

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

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

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