釘釘舊版伺服器端SDK支援非同步方法的升級改造

2023-09-13 09:00:32

最近專案中需要對接釘釘,有些釘釘 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

安裝方法

兩種方法:

  • 直接下載原始碼,然後編譯安裝。
  • 通過 Nuget 公共倉庫安裝:FireflySoft.TopSdk

使用方法

所有業務類的定義都沒有改變,而且原來的同步方法可以繼續使用。

這裡重點看下非同步方法的使用,只需要替換幾個名字:

  • IDingTalkClient 替換為 IAsyncDingTalkClient。
  • DefaultDingTalkClient 替換為 AsyncDefaultDingTalkClient 。
  • Execute 替換為 ExecuteAsync。

下面是程式碼範例:

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、雖然本人使用正常,但未做全面測試,正式使用前請謹慎測試評估,因使用此倉庫程式碼造成的損失,本人概不負責。