【问题标题】:Deadlock reading async response content from async DelegatingHandler in webapi死锁从 webapi 中的 async DelegatingHandler 读取异步响应内容
【发布时间】:2014-06-20 11:16:30
【问题描述】:

问题

我在 WebAPI 中使用 DelegatingHandler 来读取响应内容并将其审核到数据库中。

当代码到达 .ReadAsStringAsync() 时,它似乎会锁定并阻止其他请求完成。我只知道这一点,因为当我删除违规行时,它可以完美运行。

public static void Register(HttpConfiguration config)
{
    config.MessageHandlers.Add(new ResourceAuditHandler());
}

public class ResourceAuditHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                           CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        string content = "";
        if (response.Content != null)
        {
            //THIS SEEMS TO CREATE DEADLOCK!
            content = await response.Content.ReadAsStringAsync(); 
        }

        AuditResponseToDatabase(content);

        return response;
    }
}

可能的原因

我已经读到,当应用程序的两个位试图从同一个请求/响应(一个 DelegatingHandler 和 Controller/ModelBinder)读取内容时,可能会发生这种情况......或者更具体地说,当一个人试图读取 .Result 的异步响应,但另一个实例已经读取了结果/响应流,则为空。

我的第一个请求命中一个简单的控制器,但第二个请求命中一个具有属性的控制器,该属性检查 Action 参数“id”以检查其不为空。我已经读过,当 DelegatingHandler 和控制器 ModelBinder 都试图从响应中读取时,你会遇到死锁。

替代方法(同样失败)

我也尝试过使用 .ReadAsByteArrayAsync() 的其他方法(如在其他 SO 问题中所见),但这似乎仍然锁定。我知道这种方法不使用 .Result 方法,而是直接从响应流中读取(不知何故)。

 var response = await base.SendAsync(request, cancellationToken);

 string content= "";
 if (response.Content != null)
 {
     var bytes = await response.Content.ReadAsByteArrayAsync();
     responseContent = Encoding.UTF8.GetString(bytes);
 }

 AuditResponseToDatabase(content);

请帮忙!

【问题讨论】:

  • 如果您在代码中的其他任何位置(在外部调用堆栈上)有 task.Wait()task.Result,通常会发生这种情况。是这样吗?
  • @peter.swallow:你能创建一个可靠地重现问题的小例子吗?

标签: c# async-await httphandler dotnet-httpclient


【解决方案1】:

彼得,首先,您对造成这种僵局的原因是正确的。尝试一直使用async/await。换句话说,不要阻塞async 代码。您应该做的另一件事是使用.ConfigureAwait(false)

    var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);

    string content = "";
    if (response.Content != null)
    {
        content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 
    }

您可以在link找到更多信息

如果以上都不起作用,请提供一个完整的例子,好吗?

希望这会有所帮助!

【讨论】:

  • 如果他没有访问 Result 或调用 Wait() 这将无济于事。此外,ConfigureAwait(false) 在 ASP.NET 中的含义与在 UI 或控制台应用程序中的含义不同。 stackoverflow.com/questions/13489065/…
  • 这确实解决了我的问题。不幸的是,它突出了第二个问题,即使用 .ConfigureAwait(false) 似乎丢失了我的 IOC (Autofac) 所需的上下文(在本例中为 HttpContext)。有没有其他方法可以保留上下文?
  • 你可以搜索 SynchronizationContext 主题......它可能(或没有)帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多