【问题标题】:Asynchronous method is not returning in an MVC authorization filter异步方法未在 MVC 授权过滤器中返回
【发布时间】:2018-11-10 22:30:46
【问题描述】:

我有一个测试应用程序,其中类似于以下的代码正在运行,但类似代码似乎在另一个应用程序中失败:

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
    var user = GetUser().GetAwaiter().GetResult();
}

private async Task<User> GetUser()
{
    var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
    client.BaseAddress = new Uri(baseUrl);
    client.Timeout = Timeout.InfiniteTimeSpan;
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

    var request = new UserSearchRequest
        {
            DomainName = "my-corp-domain\my-user-id"
        };

    var response = await client.PostAsJsonAsync("api/v1/users/search", request);
    var users = (await response.Content.ReadAsAsync<List<User>>());
    return users.FirstOrDefault();
}   

在问题应用程序中, PostAsJsonAsync 被调用但永远不会返回。知道问题可能是什么或如何调试吗?

【问题讨论】:

标签: c# .net asp.net-mvc asynchronous


【解决方案1】:

免责声明:在同步方法中执行异步方法通常会导致死锁和性能问题。完全异步的调用路径,甚至是完全同步的路径,是首选。如果您绝对必须阻止异步方法,请继续阅读。

我遇到过类似的情况,HttpClient 请求永远不会返回,答案通常是死锁。 @Eser 提供了另一个问题的链接,该问题很好地描述了死锁过程。总而言之,对GetResult() 的调用阻塞了线程。同时,PostAsJsonAsync 正在等待同一个线程解锁,以便它可以完成处理,从而产生死锁。

An async/await example that causes a deadlock

一种可能性是使用ConfigureAwait(false)。这告诉PostAsJsonAsync 它可以在不同的上下文上完成处理,而不是等待原始上下文解锁。 GetUser() 函数将如下所示:

private async Task<User> GetUser()
{
    var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
    client.BaseAddress = new Uri(baseUrl);
    client.Timeout = Timeout.InfiniteTimeSpan;
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
    new MediaTypeWithQualityHeaderValue("application/json"));

    var request = new UserSearchRequest
    {
        DomainName = "my-corp-domain\my-user-id"
    };

    var response = await client.PostAsJsonAsync("api/v1/users/search", request).ConfigureAwait(false);
    var users = await response.Content.ReadAsAsync<List<User>>().ConfigureAwait(false);
    return users.FirstOrDefault();
}   

我过去使用的另一个解决方案是将同步执行包装在Task.Run 中。这会将 async 方法的开始推送到不同的上下文中,因此这两种方法不会尝试阻塞同一个方法。 AuthorizeCore 方法将如下所示:

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
    var user = Task.Run(() => GetUser()).GetAwaiter().GetResult();
}

有关此方法的更多信息,请参阅Should I expose synchronous wrappers for asynchronous methods?

我再次强调,两者都不是完美的解决方案。通过执行“异步同步”操作,您将失去异步功能的所有好处,同时引入更高的死锁机会和可能更差的性能。我鼓励尽可能使用异步。如果不是,坚持使用“旧”同步方法并不是最糟糕的选择。

【讨论】:

  • 不,没有可靠的解决方案可以在 MVC 操作过滤器中运行异步代码,正如我在问题的 cmets 中所描述的那样。像以前那样玩弄并试图强迫它会导致问题。
  • 我同意在任何同步方法中都没有可靠的方法来运行异步代码。我并不是建议以任何方式都需要“异步同步”。我的回答专门针对从同步方法调用时异步方法未返回的情况。
  • 当然有可靠的方法——你只需要咳出一个线程(任务)然后你就失去了异步的所有好处。
  • @Benj 问题是你的答案是教别人如何做异步而不是同步,这是不可靠的。您应该删除答案,因为它在这里不适用。
  • @bommelding 您不应该在 ASP.NET 应用程序中启动自己的线程。
猜你喜欢
  • 2018-11-16
  • 1970-01-01
  • 1970-01-01
  • 2019-09-12
  • 2014-10-04
  • 1970-01-01
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多