带有返回内容的 Task<string> 的 C# httpclient

Posted

技术标签:

【中文标题】带有返回内容的 Task<string> 的 C# httpclient【英文标题】:C# httpclient with Task<string> that return content 【发布时间】: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,不幸的是,只有你才能知道哪种方法最适合解决你的问题。 【参考方案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 肯定有编译过的代码。 是的,对不起,大卫,我的错误,我删除了等待尝试的东西,但忘记再写一遍

以上是关于带有返回内容的 Task<string> 的 C# httpclient的主要内容,如果未能解决你的问题,请参考以下文章

多线程等待返回

Task 使用

使用 async/await 并从 ASP.NET Web API 方法返回 Task<HttpResponseMessage>

.NET6之MiniAPI:Response

我从只返回 Task 而不是 Task<T> 的方法返回啥?

Task