使用delphi+intraweb進行微信開發5—準備實現微信API,先從獲取AccessToken開始 使用delphi+intraweb進行微信開發5—準備實現微信API,先從獲取AccessToken開始
https://www.cnblogs.com/dpower/
看這個部落格
使用delphi+intraweb進行微信開發5—準備實現微信API,先從獲取AccessToken開始
Posted on 2016-03-30 15:46 Delphi力量 閱讀(1157) 評論(1) 編輯 收藏在前4講中我們已經使iw開發的應用成功和微信進行了對接,再接下來的章節中我們開始逐一嘗試和實現微信的各個API,開始前先來點準備工作 |
- 首先需要明確的是,微信的API都是通過https呼叫實現的,分為post方法
- 微信方法呼叫均需傳遞AccessToken(URL引數方式),這個AccessToken不是我們微信接入時使用的Token,這個AccessToken專門用於微信API呼叫,AccessToken有過期時間,而且每天有請求次數限制,據說是為了防止不良的程式呼叫導致微信伺服器出現異常。因此在這種情況下則必須在獲取AccessToken後進行儲存,在即將過期前再重新獲取。
好吧,讓我們開始:首先定義post和get方法。這裡我們採用Indy實現,不需要再安裝什麼第三方元件了,也沒有大量併發的要求(咱們這是客戶端程式),簡單易用最重要。 |
/// <summary>
/// 向指定URL發起Get請求
/// </summary>
/// <param name="http">TIdHTTP</param>
/// <param name="URL">指定URL</param>
/// <param name="Max">Get請求失敗最大重試次數</param>
/// <returns>返回騰訊伺服器響應(string型別的json格式資料)</returns>
function GetMethod(http: TIdHTTP; URL: String; Max: Integer): String;
var
RespData: TStringStream;
begin
RespData := TStringStream.Create('', TEncoding.UTF8);
try
try
http.Get(URL, RespData);
http.Request.Referer := URL;
Result := RespData.DataString;
except
Dec(Max);
if Max = 0 then
begin
Result := '';
exit;
end;
Result := GetMethod(http, URL, Max);
end;
finally
FreeAndNil(RespData);
end;
end;
/// <summary>
/// 向指定URL提交資料(Post)
/// </summary>
/// <param name="http">TIdHTTP</param>
/// <param name="URL">指定URL</param>
/// <param name="Data">要提交的資料(UTF8String)</param>
/// <param name="Max">Post請求失敗最大重試次數</param>
/// <returns>返回騰訊伺服器響應(string型別的json格式資料)</returns>
function PostMethod(http: TIdHTTP; URL: String; Data: UTF8String;
Max: Integer): String;
var
PostData, RespData: TStringStream;
begin
RespData := TStringStream.Create('');
PostData := TStringStream.Create(Data);
try
try
if http = nil then
exit;
http.Post(URL, PostData, RespData);
Result := RespData.DataString;
http.Request.Referer := URL;
except
Dec(Max);
if Max = 0 then
begin
Result := '';
exit;
end;
Result := PostMethod(http, URL, Data, Max);
end;
finally
http.Disconnect;
FreeAndNil(RespData);
FreeAndNil(PostData);
end;
end;
有了上面兩個方法我們就可以開始測試微信API了。
♥ 不過你有沒有注意到,微信請求是https,不是http啊,所以似乎還需要讓Indy支援ssl傳輸才行啊,這當然沒有問題,上Indy官網下載SSL支援DLL即可,分為64位和32位版本不要搞錯,下載後和編譯好的IW程式放置在同一目錄下即可(說實在的下載網站我給忘了,大家可以百度一下,如果找不到給我留言,我把我下載的發出來)。
接下來研究下如何獲取這個AccessToken |
由於AccessToken在API呼叫中都需要使用,因此先來獲取AccessToken吧,關於AccessToken的解釋請看微信文件:http://mp.weixin.qq.com/wiki/14/9f9c82c1af308e3b14ba9b973f99a8ba.html
使用的微信命令URL是:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
採用Get方法。
如你所見:
1、URL呼叫需傳遞【APPID】和【APPSECRET】兩個引數,返回結果為Json格式字串。
2、呼叫成功的情況下,微信會返回下述JSON資料包給公眾號:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
引數 | 說明 |
---|---|
access_token | 獲取到的憑證 |
expires_in | 憑證有效時間,單位:秒 |
3、錯誤時微信會返回錯誤碼等資訊,JSON資料包示例如下(該示例為AppID無效錯誤):
{"errcode":40013,"errmsg":"invalid appid"}
♥ 好吧,好像還得找個Json解析的元件,嗯嗯,夠麻煩的,推薦使用第三方元件“SuperObject”進行Json格式解析,不過你要非得自行進行Json字串編解碼也行,嗯,可能會累點,相信我,最好找個封裝完善的Json元件,工欲善其事必先利其器,否則建立選單什麼的時候有你受的。
嗯,Json結果解析判斷什麼的我就不說了,再說說AccessToken的快取問題。 |
簡單說:“為了保密appsecrect,第三方需要一個access_token獲取和重新整理的中控伺服器。而其他業務邏輯伺服器所使用的access_token均來自於該中控伺服器,不應該各自去重新整理,否則會造成access_token覆蓋而影響業務;”這個是微信文件原話,所以讓我們看看怎麼快取這個東東。
先宣告一個TAccessToken記錄,然後利用一個全域性公開的變數,再加上一個泛型容器,如此的簡單:
/// <summary>
/// AccessToken記錄,包含AccessToken值和過期時間
/// </summary>
TAccessToken = record
AccessToken: string;
AccessTokenExpiresDt: TDateTime;
end;
var
gAccessTokenLst: TDictionary<string, TAccessToken>;
gCS: TCriticalSection;
♥ 宣告成記錄型別的好處是,可以當做簡單變數來使用,要是宣告成指標或者類,哦哦,建立再釋放,太麻煩了。微信返回的json結果中是這個AccessToken還有多少秒過期,我們換算成時間型別會比較好用一些。
♥ 泛型我喜歡,讓Hash表成為強型別的。
♥ 如上gAccessTokenLst用來儲存獲取的AccessToken,key就是微訊號的APPID,估計這個不會重複吧!上面還聲明瞭一個臨界區物件gCS,大約我不說你也能猜到,既然是全域性的那就得上鎖,防止寫入混亂。
♥ 上面那兩個變數gAccessTokenLst和gCS我是在單元的initialization部分例項化的,並在finalization進行了釋放,在這兩個地方處理全域性變數的好處是:執行時只執行一次。是初始化全域性變數的絕佳地點。
萬事具備了,最終獲取AccessToken的函式程式碼如下: |
procedure TWxSdkImp.GetAccessToken(const appid, appsecret: string;
var AccessToken: string; const GetNew: Boolean = false);
var
URL: string;
JSONObject: ISuperObject;
temp, sKey: string;
recAccessToken: TAccessToken;
begin
if (appid = '') or (appsecret = '') then
raise Exception.Create('TWxSdkImp.GetAccessToken執行出錯,引數應用ID或者應用祕鑰不能為空!');
recAccessToken.AccessToken := '';
sKey := appid;
if gAccessTokenLst.ContainsKey(sKey) then
recAccessToken := gAccessTokenLst.Items[sKey];
// 如果要求重新獲取AccessToken 或者 尚未獲取AccessToken 或者 已經獲取了但是離過期不足30秒
gCS.Enter;
try
if GetNew or (recAccessToken.AccessToken = '') or (SecondSpan(recAccessToken.AccessTokenExpiresDt, Now) < 30) then
begin
URL := Format(WxCmdUrl_GetAccessToken, [appid, appsecret]);
temp := GetMethod(http, URL, 3);
JSONObject := ParseJson(temp, ['"access_token"', '"errcode"']);
if Pos('"access_token"', temp) > 0 then
begin
recAccessToken.AccessToken := JSONObject['access_token'].AsString;
recAccessToken.AccessTokenExpiresDt := IncSecond(Now, JSONObject['expires_in'].AsInteger);
if gAccessTokenLst.ContainsKey(sKey) then
gAccessTokenLst.Remove(sKey);
gAccessTokenLst.Add(sKey, recAccessToken);
end else
raise Exception.Create('TWxSdkImp.GetAccessToken執行出錯,伺服器返回錯誤程式碼:' + JSONObject['errcode'].AsString + ',錯誤資訊:' + JSONObject['errmsg'].AsString + '!'