[英]Why can't I use HttpClient for Syncrhonous calls from ASP.Net?
我正在使用客戶端庫來訪問 3rd 方 API。 該庫由 NSwagStudio 從 Swagger 文檔生成。
我正在開發的應用程序在其所有調用中完全同步,將其更新為異步超出了我的工作范圍。
當我通過單元測試測試客戶端庫時,它工作正常。 當我嘗試從 ASP.Net 應用程序中調用它時,出現以下錯誤:
CancellationTokenSource 已被處理。
我已經將客戶端庫提煉為演示問題的基本要素,我選擇了一個選項來提供同步方法以及異步:
public class ClientApi
{
private readonly HttpClient _httpClient;
public ClientApi(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string BaseUrl { get; set; }
public object Get()
{
return Task.Run(async () => await GetAsync(CancellationToken.None)).GetAwaiter().GetResult();
}
/// <returns>OK</returns>
/// <param name="cancellationToken">
/// A cancellation token that can be used by other objects or threads to receive notice of
/// cancellation.
/// </param>
public async Task<string> GetAsync(CancellationToken cancellationToken)
{
var client_ = _httpClient;
try
{
using (var request_ = new HttpRequestMessage())
{
request_.Method = new HttpMethod("GET");
request_.RequestUri = new System.Uri(BaseUrl, System.UriKind.RelativeOrAbsolute);
var response_ = await client_.SendAsync(
request_,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
).ConfigureAwait(false);
try
{
// Exception occurs on following line
var responseData_ = response_.Content == null
? null
: await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
return responseData_;
}
finally
{
response_?.Dispose();
}
}
}
finally { }
}
}
這是調用它的代碼:
protected void OnClick(object sender, EventArgs e)
{
var httpClient = new HttpClient();
var client = new ClientApi(httpClient)
{
BaseUrl = "https://www.google.com"
};
var html = client.Get();
}
調用它的代碼只是一個帶有按鈕的asp.net 頁面,按鈕事件運行的代碼與通過的單元測試相同。
當我比較調試器中的運行時:從單元測試中, response_.Content 對象沒有取消標記,但是當從 asp.net 運行時它有。 事實上,它們幾乎似乎是不同的對象,盡管 GetType() 將它們報告為 System.Net.Http.StreamContent。 從反編譯類來看,它沒有 _cancellationtoken 屬性,那么調試器從哪里獲取它呢?
我猜對我的 asp.net web 應用程序的 http 請求有它自己的令牌和源,它以某種方式被 HttpClient 使用。 但是,客戶端正在等待所有異步調用以同步獲取結果,因此我不明白如何處理底層 CTS,因為我們還沒有從客戶端庫的調用中返回。
任何人都可以理解發生了什么並且有解決方案嗎?
首先,你真的應該重新考慮重寫你的客戶端應用程序,這樣你就可以一直實現異步。
“一路異步”意味着你不應該在沒有仔細考慮后果的情況下混合同步和異步代碼。 特別是,通過調用 Task.Wait 或 Task.Result 來阻止異步代碼通常是一個壞主意。
取自這個偉大的指南。
基本上,通過運行異步代碼同步,你總是會做錯事。
但是,如果您確實需要一種解決方案,請首先將一次性對象包裝在 using 語句中,而不是手動處理它們。 這是您的 ClientApi 類的簡化解決方案,它可以滿足您的需求(但它可能會死鎖)。 代碼與此答案中的代碼基本相同。
public class ClientApi
{
public object Get(string url)
{
using (var client = new HttpClient())
{
var response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
return responseContent.ReadAsStringAsync().Result;
}
}
}
}
在此處閱讀有關死鎖的更多信息
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.