1. 程式人生 > >使用delphi+intraweb進行微信開發5—準備實現微信API,先從獲取AccessToken開始 使用delphi+intraweb進行微信開發5—準備實現微信API,先從獲取AccessToken開始

使用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,開始前先來點準備工作
  1. 首先需要明確的是,微信的API都是通過https呼叫實現的,分為post方法
    呼叫和get方法呼叫。不需要上傳資料的採用get方法(例如獲取AccessToken),而需要向微信伺服器提交資料的採用post方法(例如建立選單)。
  2. 微信方法呼叫均需傳遞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(httpTIdHTTPURLStringMaxInteger): String;

var 
  RespDataTStringStream
begin 
  RespData := TStringStream.Create(''TEncoding.UTF8); 
  try 
    try 
      http.Get(URLRespData); 
      http.Request.Referer := URL
      Result := RespData.DataString
    except 
      Dec(Max); 
      if Max then 
      begin 
        Result := ''
        exit
      end
      Result := GetMethod(httpURLMax); 
    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(httpTIdHTTPURLStringDataUTF8String
    MaxInteger): String

var 
  PostDataRespDataTStringStream
begin 
  RespData := TStringStream.Create(''); 
  PostData := TStringStream.Create(Data); 
  try 
    try 
      if http nil then 
        exit
      http.Post(URLPostDataRespData); 
      Result := RespData.DataString
      http.Request.Referer := URL
    except 
      Dec(Max); 
      if Max then 
      begin 
        Result := ''
        exit
      end
      Result := PostMethod(httpURLDataMax); 
    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 
  AccessTokenstring
  AccessTokenExpiresDtTDateTime
end;

var 
  gAccessTokenLstTDictionary<stringTAccessToken>; 
  gCSTCriticalSection;

♥  宣告成記錄型別的好處是,可以當做簡單變數來使用,要是宣告成指標或者類,哦哦,建立再釋放,太麻煩了。微信返回的json結果中是這個AccessToken還有多少秒過期,我們換算成時間型別會比較好用一些。

♥ 泛型我喜歡,讓Hash表成為強型別的。

♥  如上gAccessTokenLst用來儲存獲取的AccessToken,key就是微訊號的APPID,估計這個不會重複吧!上面還聲明瞭一個臨界區物件gCS,大約我不說你也能猜到,既然是全域性的那就得上鎖,防止寫入混亂。

♥  上面那兩個變數gAccessTokenLst和gCS我是在單元的initialization部分例項化的,並在finalization進行了釋放,在這兩個地方處理全域性變數的好處是:執行時只執行一次。是初始化全域性變數的絕佳地點。

 

萬事具備了,最終獲取AccessToken的函式程式碼如下:

 

procedure TWxSdkImp.GetAccessToken(const appidappsecretstring
  var AccessTokenstringconst GetNewBoolean = false); 
var 
  URLstring
  JSONObjectISuperObject
  tempsKeystring
  recAccessTokenTAccessToken
begin 
  if (appid ''or (appsecret ''then 
    raise Exception.Create('TWxSdkImp.GetAccessToken執行出錯,引數應用ID或者應用祕鑰不能為空!'); 

  recAccessToken.AccessToken := ''
  sKey := appid
  if gAccessTokenLst.ContainsKey(sKeythen 
    recAccessToken := gAccessTokenLst.Items[sKey]; 

  // 如果要求重新獲取AccessToken 或者 尚未獲取AccessToken 或者 已經獲取了但是離過期不足30秒 
  gCS.Enter
  try 
    if GetNew or (recAccessToken.AccessToken ''or (SecondSpan(recAccessToken.AccessTokenExpiresDtNow30then 
    begin 
      URL := Format(WxCmdUrl_GetAccessToken[appidappsecret]); 
      temp := GetMethod(httpURL3); 
      JSONObject := ParseJson(temp['"access_token"''"errcode"']); 
      if Pos('"access_token"'tempthen 
      begin 
        recAccessToken.AccessToken := JSONObject['access_token'].AsString
        recAccessToken.AccessTokenExpiresDt := IncSecond(NowJSONObject['expires_in'].AsInteger); 

        if gAccessTokenLst.ContainsKey(sKeythen 
          gAccessTokenLst.Remove(sKey); 
        gAccessTokenLst.Add(sKeyrecAccessToken); 
      end else 
        raise Exception.Create('TWxSdkImp.GetAccessToken執行出錯,伺服器返回錯誤程式碼:' JSONObject['errcode'].AsString ',錯誤資訊:' JSONObject['errmsg'].AsString '!'); 
    end
  finally 
    gCS.Leave
  end
  AccessToken := recAccessToken.AccessToken
end;

需要獲取AccessToken時,

var accessToken: string; 

GetAccessToken('YourAppID', 'YourAppSecret', accessToken);

輸出的accessToken字串就是我們想要的結果。

在前4講中我們已經使iw開發的應用成功和微信進行了對接,再接下來的章節中我們開始逐一嘗試和實現微信的各個API,開始前先來點準備工作
  1. 首先需要明確的是,微信的API都是通過https呼叫實現的,分為post方法呼叫和get方法呼叫。不需要上傳資料的採用get方法(例如獲取AccessToken),而需要向微信伺服器提交資料的採用post方法(例如建立選單)。
  2. 微信方法呼叫均需傳遞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(httpTIdHTTPURLStringMaxInteger): String;

var 
  RespDataTStringStream
begin 
  RespData := TStringStream.Create(''TEncoding.UTF8); 
  try 
    try 
      http.Get(URLRespData); 
      http.Request.Referer := URL
      Result := RespData.DataString
    except 
      Dec(Max); 
      if Max then 
      begin 
        Result := ''
        exit
      end
      Result := GetMethod(httpURLMax); 
    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(httpTIdHTTPURLStringDataUTF8String
    MaxInteger): String

var 
  PostDataRespDataTStringStream
begin 
  RespData := TStringStream.Create(''); 
  PostData := TStringStream.Create(Data); 
  try 
    try 
      if http nil then 
        exit
      http.Post(URLPostDataRespData); 
      Result := RespData.DataString
      http.Request.Referer := URL
    except 
      Dec(Max); 
      if Max then 
      begin 
        Result := ''
        exit
      end
      Result := PostMethod(httpURLDataMax); 
    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 
  AccessTokenstring
  AccessTokenExpiresDtTDateTime
end;

var 
  gAccessTokenLstTDictionary<stringTAccessToken>; 
  gCSTCriticalSection;

♥  宣告成記錄型別的好處是,可以當做簡單變數來使用,要是宣告成指標或者類,哦哦,建立再釋放,太麻煩了。微信返回的json結果中是這個AccessToken還有多少秒過期,我們換算成時間型別會比較好用一些。

♥ 泛型我喜歡,讓Hash表成為強型別的。

♥  如上gAccessTokenLst用來儲存獲取的AccessToken,key就是微訊號的APPID,估計這個不會重複吧!上面還聲明瞭一個臨界區物件gCS,大約我不說你也能猜到,既然是全域性的那就得上鎖,防止寫入混亂。

♥  上面那兩個變數gAccessTokenLst和gCS我是在單元的initialization部分例項化的,並在finalization進行了釋放,在這兩個地方處理全域性變數的好處是:執行時只執行一次。是初始化全域性變數的絕佳地點。

 

萬事具備了,最終獲取AccessToken的函式程式碼如下:

 

procedure TWxSdkImp.GetAccessToken(const appidappsecretstring
  var AccessTokenstringconst GetNewBoolean = false); 
var 
  URLstring
  JSONObjectISuperObject
  tempsKeystring
  recAccessTokenTAccessToken
begin 
  if (appid ''or (appsecret ''then 
    raise Exception.Create('TWxSdkImp.GetAccessToken執行出錯,引數應用ID或者應用祕鑰不能為空!'); 

  recAccessToken.AccessToken := ''
  sKey := appid
  if gAccessTokenLst.ContainsKey(sKeythen 
    recAccessToken := gAccessTokenLst.Items[sKey]; 

  // 如果要求重新獲取AccessToken 或者 尚未獲取AccessToken 或者 已經獲取了但是離過期不足30秒 
  gCS.Enter
  try 
    if GetNew or (recAccessToken.AccessToken ''or (SecondSpan(recAccessToken.AccessTokenExpiresDtNow30then 
    begin 
      URL := Format(WxCmdUrl_GetAccessToken[appidappsecret]); 
      temp := GetMethod(httpURL3); 
      JSONObject := ParseJson(temp['"access_token"''"errcode"']); 
      if Pos('"access_token"'tempthen 
      begin 
        recAccessToken.AccessToken := JSONObject['access_token'].AsString
        recAccessToken.AccessTokenExpiresDt := IncSecond(NowJSONObject['expires_in'].AsInteger); 

        if gAccessTokenLst.ContainsKey(sKeythen 
          gAccessTokenLst.Remove(sKey); 
        gAccessTokenLst.Add(sKeyrecAccessToken); 
      end else 
        raise Exception.Create('TWxSdkImp.GetAccessToken執行出錯,伺服器返回錯誤程式碼:' JSONObject['errcode'].AsString ',錯誤資訊:' JSONObject['errmsg'].AsString '!'); 
    end
  finally 
    gCS.Leave
  end
  AccessToken := recAccessToken.AccessToken
end;