HttpClient 请求在 asp .net 项目中不起作用

ape*_*als 1 api asp.net-mvc request console-application dotnet-httpclient

我正在使用 HttpClient 向 api 发出请求。此代码位于与两个附加项目(控制台和 Asp.Net Mvc 项目)共享的类库项目中。当我从控制台项目发出请求时,它工作得很好,但在 asp 项目中,它阻塞了行

using(Stream responseStream = await response.Content.ReadAsStreamAsync()
Run Code Online (Sandbox Code Playgroud)

这是我的请求代码

private async Task<dynamic> ReadJson(string url)
        {
            HttpResponseMessage response = await httpClient.GetAsync(url);

            if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
                throw new RateLimitException();

            if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
                throw new AccessDeniedException();

            if (response.StatusCode != System.Net.HttpStatusCode.OK)
                throw new Exception("Error: " + response.StatusCode);

            using (Stream responseStream = await response.Content.ReadAsStreamAsync())
            using (StreamReader sr = new StreamReader(responseStream, System.Text.Encoding.UTF8))
            {
                string json = sr.ReadToEnd();
                return JObject.Parse(json);
            }
        }
Run Code Online (Sandbox Code Playgroud)

我正在从控制台和 Asp.Net 项目对方法进行相同的调用。从控制台工作,但 asp .net 项目在读取响应内容时阻止行

Ale*_*rt. 6

这种死锁的发生很可能是因为调用ReadJson函数的控制器操作是同步的。您需要使操作异步。您可以在此处找到对此僵局的极好解释。(所有学分归斯蒂芬·克利里所有)

快速总结是:

/ My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public class MyController : ApiController
{
  public string Get()
  {
    var jsonTask = GetJsonAsync(...);
    return jsonTask.Result.ToString();
  }
}
Run Code Online (Sandbox Code Playgroud)

导致死锁的原因

  1. 顶级方法调用 GetJsonAsync(在 UI/ASP.NET 上下文中)。GetJsonAsync 通过调用 HttpClient.GetStringAsync(仍在上下文中)启动 REST 请求。

  2. GetStringAsync 返回一个未完成的 Task,表示 REST 请求未完成。

  3. GetJsonAsync 等待 GetStringAsync 返回的 Task。上下文被捕获,稍后将用于继续运行 GetJsonAsync 方法。GetJsonAsync 返回一个未完成的Task,说明GetJsonAsync 方法未完成。

  4. 顶层方法同步阻塞 GetJsonAsync 返回的 Task。这会阻塞上下文线程。

  5. … 最终,REST 请求将完成。这完成了由 GetStringAsync 返回的任务。

  6. GetJsonAsync 的延续现在已准备好运行,它等待上下文可用,以便它可以在上下文中执行。

  7. 僵局。顶层方法正在阻塞上下文线程,等待 GetJsonAsync 完成,而 GetJsonAsync 正在等待上下文空闲以便它可以完成。

防止死锁

有两种最佳做法可以避免这种情况:

  1. 在您的“库”异步方法中,尽可能使用 ConfigureAwait(false)。
  2. 不要阻塞任务;一直使用异步。