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

C# Xamarin does not receive response from HttpClient

我在 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,很多响应从服务器返回,但函数没有返回。应用程序冻结。

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

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

感谢您的回答!

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

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

然后当实际的 SendAsync 方法完成其工作时,它将尝试 return 到那个 SynchronizationContext。这通常没问题。但是,这实际上取决于您实际调用封装方法的方式。

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

那么在这种情况下简单地 returning 任务有什么用呢?由于您没有等待 Task 并且该方法未标记为异步,因此上下文切换的责任已移至堆栈中调用 _SendAsync 的位置。在这里调用 .Result 不会进行上下文切换,而只会进行阻塞调用并失去开始时执行 async/await 的所有好处。

或者,您也可以将 .ConfigureAwait(false) 添加到等待的 this._Client.SendAsync() 呼叫中。这是做什么的,不是 return 到捕获 SynchronizationContext 而是 return 到它执行的任何 ThreadPool 线程。

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

通常应等待任务。或者,如果您真的不关心结果,您可以使用 Task.Run().

解雇并忘记

大多数不需要 return 到捕获的上下文的服务调用和异步调用,大部分可以 post-固定为 ConfigureAwait(false)。没有它的主要原因是如果你想在之后更新 UI。

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

我强烈建议您阅读有关 async/await in the MS Docs

的优秀文档

长话短说:

要么:

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。永远等待他们。