【问题标题】:Why can't I use HttpClient for Syncrhonous calls from ASP.Net?为什么我不能使用 HttpClient 从 ASP.Net 进行同步调用?
【发布时间】:2019-02-16 03:41:31
【问题描述】:

我正在使用客户端库来访问第 3 方 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,因为我们还没有从客户端库的调用中返回。

任何人都可以理解发生了什么并且有解决方案吗?

【问题讨论】:

  • 您可能不想同时使用 await + GetResult/.Result。这种组合很容易死锁
  • 这不是我的代码,因为它是由 NSwag Studio 生成的。同步调用异步方法以避免潜在死锁@steve 的推荐方法是什么?
  • 你没有。一路同步或一路异步。混合它们很容易导致死锁
  • 将事件处理程序更改为异步应该很容易。这是一个事件,框架事件可以是 async void 是可以的(实际上这是少数可以的情况之一)。

标签: c# asp.net httpclient cancellation-token sendasync


【解决方案1】:

首先,您应该真正重新考虑重写您的客户端应用程序,以便您可以实现异步

“一路异步”意味着你不应该混合同步和 异步代码没有仔细考虑后果。在 特别是,通过调用来阻塞异步代码通常是个坏主意 Task.Wait 或 Task.Result。

取自this伟大的指南。

基本上,通过运行异步代码同步,您总是会做错事。

但是,如果您真的需要一种解决方案,请先将一次性对象包装在 using 语句中,而不是手动处理它们。 这是您的 ClientApi 类的简化解决方案,它可以满足您的需要(但它可能会死锁)。代码与answer中的代码基本相同。

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;
            }
        }

    }
}

阅读更多关于死锁的信息here

【讨论】:

  • 我读过的关于 HttpClient 的所有内容都表明它应该存在于应用程序的生命周期中,并且为每个请求创建一个新实例是一个坏主意,当你的应用程序开始发出大量请求时会导致套接字问题.我在生成允许我注入客户端的客户端时特别选择了一个选项,所以我可以这样做。
  • 我已经放弃了生成的客户端,现在正在使用我使用 RestSharp 编写的一个简单的客户端,并且只同步调用。但是,我提出了这个问题,因为我希望了解我在这个特定示例中看到这种行为的原因。一个对象 (StreamContent) 有时如何具有属性而不是其他时候。这让我感到困惑,并且违背了我对工作语言的理解,所以会喜欢一些启示!
猜你喜欢
  • 2013-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-14
  • 1970-01-01
相关资源
最近更新 更多