簡體   English   中英

為什么我不能使用 HttpClient 進行來自 ASP.Net 的同步調用?

[英]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

從單元測試

我猜對我的 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM