【问题标题】:C# httpclient with Task<string> that return content带有返回内容的 Task<string> 的 C# httpclient
【发布时间】:2018-01-08 14:07:47
【问题描述】:

我有以下功能,但无法调试它,因为响应永远不会到来。另一边的服务正在工作。任何帮助都会受到重视,我无法推断出必须如何处理 SO 中的其他答案

    public async Task<string> PostObjectToWebServiceAsJSON(object objectToPost, string validatorName, string method)
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("myuri" + "/" + method);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            // HTTP POST
            var response = await client.PostAsJsonAsync("", objectToPost);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                string errplugin = "Error";

                return null;
            }
        }
    }

我是这样称呼它的:

    public PaymentResponseInternal Post(some stuff here)
    {
        Task<string> retornoPluginAsync = PostObjectToWebServiceAsJSON(some stuff here);

        retornoPluginAsync.Wait();
        string result = retornoPluginAsync.Result;

        return JsonConvert.DeserializeObject<PaymentResponseInternal>(result);
    }

【问题讨论】:

  • 当您说“响应永远不会到来”时,您是什么意思?是挂了吗?你怎么调用这个函数?这很可能是一个僵局。
  • 是的,这是一个死锁,因为您正在调用retornoPluginAsync.Result。你需要await那个电话。见这里:blog.stephencleary.com/2012/07/dont-block-on-async-code.html
  • 听起来public PaymentResponseInternal Post(some stuff here) 应该是public async Task&lt;PaymentResponseInternal&gt; Post(some stuff here) 并且应该在内部使用await
  • 你也在等待那个函数。异步代码就像一个病毒,一旦你开始,它会慢慢消耗你的整个项目(这是一件好事)
  • 然后看看this,不幸的是,只有你才能知道哪种方法最适合解决你的问题。

标签: c# .net asynchronous async-await dotnet-httpclient


【解决方案1】:

如 cmets 中所述,您应该将 PaymentResponseInternal 设为一个返回 Task&lt;PaymentResponseInternal&gt;async 方法并等待它。

您不应该混合使用同步和异步代码。造成这种情况的原因之一是您可能会出现死锁,正如您在此处发现的那样。更多信息请参考@Stephen Cleary 的文章:https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

如果由于某种原因您无法将Post 方法设为async,您可以尝试不捕获PostObjectToWebServiceAsJSON 方法中的上下文,方法是在每个ConfigureAwait(false) 之后调用ConfigureAwait(false)

public async Task<string> PostObjectToWebServiceAsJSON(object objectToPost, string validatorName, string method)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri("myuri" + "/" + method);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var response = await client.PostAsJsonAsync("", objectToPost).ConfigureAwait(false);
        if (response.IsSuccessStatusCode)
        {
            return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        }
        else
        {
            return null;
        }
    }
}

请参阅@Stephen Cleary 的博文以了解相关信息:https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

【讨论】:

    【解决方案2】:

    随着您对问题的更新,您在同步调用PostObjectToWebServiceAsJSON 时遇到了死锁情况。您应该在 Wait() 之前添加对 retornoPluginAsync.ConfigureAwait(false); 的调用。 ConfigureAwait(false)Task 配置为不需要原始调用上下文,并且在您的情况下应该解决死锁。

        public PaymentResponseInternal Post(some stuff here)
        {
            Task<string> retornoPluginAsync = PostObjectToWebServiceAsJSON(some stuff here);
            retornoPluginAsync.ConfigureAwait(false); // Add this
            retornoPluginAsync.Wait();
            string result = retornoPluginAsync.Result;
    
            return JsonConvert.DeserializeObject<PaymentResponseInternal>(result);
        }
    

    【讨论】:

    • 我这样做,编译但不返回任何东西。
    • 其实我很怀念问题中的等待
    • 看起来问题中的代码是一个移动目标,正在使发布的回复无效......
    • 这个答案显然永远不会起作用,因为 OP 肯定有编译过的代码。
    • 是的,对不起,大卫,我的错误,我删除了等待尝试的东西,但忘记再写一遍
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    • 2017-03-20
    相关资源
    最近更新 更多