C# Xamarin 没有收到来自 HttpClient 的响应

Posted

技术标签:

【中文标题】C# Xamarin 没有收到来自 HttpClient 的响应【英文标题】:C# Xamarin does not receive response from HttpClient 【发布时间】:2020-07-20 18:42:35 【问题描述】:

我在 C# Xamarin 中有以下方法进行身份验证:

private async Task<HttpResponseMessage> _SendAsync(HttpMethod method, string url, object obj)
    
        HttpRequestMessage request = new HttpRequestMessage(method, url);
        request.Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
        if (!string.IsNullOrEmpty(this._AuthToken))
        
            request.Headers.Add("Authorization", this._AuthToken);
        
        return await this._Client.SendAsync(request);
    

服务器收到请求,认证成功(出现在服务器日志中)。基于wireshark,许多响应从服务器返回,但函数没有返回。应用程序冻结。

有什么想法吗? 此方法在普通控制台客户端上运行没有任何问题。

没有错误信息,只是一个冻结的客户端。

谢谢你的回答!

【问题讨论】:

你怎么称呼_SendAsync?另外,为什么不直接从SendAsync 返回任务,而不是在不需要立即等待时等待它呢?只需从签名和等待中删除 async 关键字。产量,类似的结果,只是包装更少。但我认为问题出在您实际调用_SendAsync 的代码中,它可能被称为错误并且无法返回它来自的上下文,从而导致死锁。 this._Client 是 HttpClient 对象的一个​​实例。我忘记写了,因为名字是一样的。 HttpClient 只有 SendAsync 方法,但我会尝试。 @Cheesebaron:你能评论为答案吗?我想接受,但如果它只是评论,我不能接受。谢谢 【参考方案1】:

您在这里遇到的是典型的死锁案例。在 cmets 中,我建议简单地删除 asyncawait 关键字并简单地返回 Task HttpClient.SendAsync 方法返回。为什么这会改变什么?让我解释一下。

就在你之前await 一个异步方法捕获当前的SynchronizationContext。在您的情况下,它可能是 UI 线程。

然后当实际的SendAsync 方法完成它的工作时,它会尝试返回到那个SynchronizationContext。这通常很好。但是,这实际上取决于您实际调用封装方法的方式。

如果您从 UI 线程执行以下操作:_SendAsync(..).Result,这很可能会导致死锁。由于.Result 是一个阻塞调用,UI 线程将等待.Result 完成。由于 UI 线程正在等待,在等待的 SendAsync 方法完成后返回 UI 线程 SynchronizationContext 将不起作用,因为 UI 线程被阻塞。

那么在这种情况下,简单地返回任务有什么作用呢?由于您没有在等待 Task 并且该方法没有标记为异步,因此上下文切换的责任被向上移动到调用 _SendAsync 的位置。在此处调用 .Result 不会进行上下文切换,而只是进行阻塞调用,并失去执行 async/await 的所有好处。

或者,您也可以将.ConfigureAwait(false) 添加到等待的this._Client.SendAsync() 调用中。这样做的目的是不返回捕获 SynchronizationContext,而只是返回执行它的任何 ThreadPool 线程。

在任何一种情况下,使用.Result.GetAwaiter().GetResult() 时都要非常小心,在大多数情况下应该避免使用。

通常应该等待任务。或者,如果您并不真正关心结果,您可以使用 Task.Run() 解雇并忘记。

大多数不需要返回捕获的上下文的服务调用和异步调用,大部分都可以使用ConfigureAwait(false) 进行后置修复。没有它的主要原因是如果您想在之后更新 UI。

您可以阅读更多关于ConfigureAwait in the FAQ by Stepen Toub的信息

我强烈建议您阅读有关 async/await in the MS Docs 的优秀文档

TL;DR:

要么做:

private async Task<HttpResponseMessage> _SendAsync(HttpMethod method, string url, object obj)

    HttpRequestMessage request = new HttpRequestMessage(method, url);
    request.Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
    if (!string.IsNullOrEmpty(this._AuthToken))
    
        request.Headers.Add("Authorization", this._AuthToken);
    
    return await this._Client.SendAsync(request).ConfigureAwait(false);

或者做:

private Task<HttpResponseMessage> _SendAsync(HttpMethod method, string url, object obj)

    HttpRequestMessage request = new HttpRequestMessage(method, url);
    request.Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
    if (!string.IsNullOrEmpty(this._AuthToken))
    
        request.Headers.Add("Authorization", this._AuthToken);
    
    return this._Client.SendAsync(request);

解决问题。

并避免在 Tasks 上调用 .Result。永远等待他们。

【讨论】:

以上是关于C# Xamarin 没有收到来自 HttpClient 的响应的主要内容,如果未能解决你的问题,请参考以下文章

来自 ios 中的 Visual Studio xamarin 的 Web 服务调用

有没有办法在 Xamarin C# 中的 ListView 的列上方创建列名

C# Xamarin - 如何延迟第二个 void

使用来自 Visual Studio (Xamarin) 的 Google Play 商店签名始终收到“无效客户端”错误

来自 SQLite 数据库 C# Android 的 Xamarin ListView 显示项目

C#异步套接字服务器没有收到来自Java客户端的响应