最近專案中需要對接釘釘,有些釘釘 API 的存取需要使用舊版伺服器端 SDK 才能搞定,但是這個 SDK 使用的還是 .NET Framework 2.0 框架,不能跨平臺部署,也不支援 async\await 的非同步操作方法,Nuget 上也有其它使用者改造的 .NET Core 版本,但是都不支援非同步方法,於是就想自己改造一下,經過若干小時的改造,最終發現完全可行,這篇文章就把改造的結果分享給大家。
主要包括兩項改造:調整框架為 .netstandard 2.0;支援非同步的Get/Post方法。
程式碼已經上傳到了公共倉庫,需要的請自取。
https://github.com/bosima/TopSdk
https://gitee.com/bosima/TopSdk
兩種方法:
所有業務類的定義都沒有改變,而且原來的同步方法可以繼續使用。
這裡重點看下非同步方法的使用,只需要替換幾個名字:
下面是程式碼範例:
IAsyncDingTalkClient client = new AsyncDefaultDingTalkClient("https://oapi.dingtalk.com/user/get");
OapiUserGetRequest req = new OapiUserGetRequest();
req.Userid = "userid1";
req.SetHttpMethod("GET");
OapiUserGetResponse rsp = await client.ExecuteAsync(req, accessToken)
給大家分享下具體怎麼做的。
修改框架為 .netstandard 2.0 ,這個網上有很多介紹,基本上沒有什麼難度。
我用了一個 Visual Studio 的外掛升級的,有興趣的可以看看:.NET Upgrade Assistant – Visual Studio Marketplace
這個部分比較麻煩一些,因為要支援 async/await 的編寫方式。
SDK原來的網路請求都是通過 HttpWebRequest 實現的,這個類十分基礎,雖然也支援非同步,但還是比較老舊的非同步回撥方式。所以我使用了 HttpClient這個新的 HTTP 操作類來替換它。
原來的 HTTP 操作都封裝在 Util/WebUtils.cs 這個檔案中,我模仿這個類建立了一個新的 Util/AsyncWebUtils.cs 。相關的屬性和公開方法都保留了下來,只是方法改成了非同步方法,方法名後邊都加上了 Async。
這裡有一點很重要的問題:HttpClient 存在DNS快取的問題,也就是使用它存取某個域名的時候,它會把這個域名對應的IP快取下來,預設情況下永遠不更新,如果網站更換了DNS解析,原來的IP可能就存取不了了。
為了解決這個問題,微軟官方在 HttpHandler 中新增了一個屬性設定 PooledConnectionLifetime,它的本來意思是設定連線池中連線的生命週期,然後通過它也可以解決DNS快取的問題,因為重新連線就要重新解析域名,就可以緩解上面這個問題。
同時為了相容原來的一些網路超時和代理的設定,我這裡選擇的 HttpHandler 是 SocketsHttpHandler。但是但是又有新的問題了,.netstandard 2.0 不支援 SocketsHttpHandler,這個要到 .NET Core 2.1才支援。不過也有解決辦法,有開發者將這個類單獨提了出來:https://github.com/TalAloni/StandardSocketsHttpHandler
到這裡 HTTP 的基本非同步操作問題就都解決了,看看 HttpClient 的初始化方法,這裡用了一個雙檢索來實現單例,因為 HttpClient 內部會建立一個連線池,所以我們沒必要每次new一個,每次new還會導致底層網路埠釋放不及時的問題。
private HttpClient GetHttpClient()
{
if (_httpClient == null)
{
lock (_lock)
{
if (_httpClient == null)
{
// https://github.com/TalAloni/StandardSocketsHttpHandler
var handler = new StandardSocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(3), // Recreate every 3 minutes
ConnectTimeout = TimeSpan.FromMilliseconds(_timeout),
ResponseDrainTimeout = TimeSpan.FromMilliseconds(_readWriteTimeout),
UseProxy = _disableWebProxy,
};
if (this._ignoreSSLCheck)
{
handler.SslOptions.RemoteCertificateValidationCallback = new RemoteCertificateValidationCallback(TrustAllValidationCallback);
}
_httpClient = new HttpClient(handler);
}
}
}
return _httpClient;
}
其它就是非同步Get、Post的實現,Post的實現又需要實現Json請求和上傳檔案的處理,最後是返回值的解碼處理,這些比較繁瑣,但是沒什麼大問題,大家有興趣的看程式碼就可以了。
1、原始碼是從釘釘開放平臺頁面公開下載的(點此前往),其中沒有 License 檔案,但是從公開下載的行為看,應該是允許修改的,且本人也沒有售賣此程式碼。如有侵權,請聯絡刪除此倉庫。
2、雖然本人使用正常,但未做全面測試,正式使用前請謹慎測試評估,因使用此倉庫程式碼造成的損失,本人概不負責。