HttpClient的PostAsync()方法从未返回响应

32

我的问题和这个问题非常相似。我有一个AuthenticationService类,它使用HttpClientPostAsync()方法,并在从ASP项目运行它时永远不返回结果,但是当我在控制台应用程序中实现它时,它可以正常工作。

这是我的认证服务类:

public class AuthenticationService : BaseService
{
    public async Task<Token> Authenticate (User user, string url)
    {
        string json = JsonConvert.SerializeObject(user);
        StringContent content = new StringContent(json, Encoding.UTF8, "application/json");

        HttpResponseMessage response = await _client.PostAsync(url, content);
        string responseContent = await response.Content.ReadAsStringAsync();
        Token token = JsonConvert.DeserializeObject<Token>(responseContent);

        return token;
    }
}

这里就是悬挂的地方:HttpResponseMessage response = await _client.PostAsync(url, content);

这是我的控制器调用服务的地方:

public ActionResult Signin(User user)
{
    // no token needed to be send - we are requesting one
    Token token =  _authenticationService.Authenticate(user, ApiUrls.Signin).Result;
    return View();
}

以下是我使用Console应用程序测试该服务的示例,结果运行良好。

class Program
{
    static void Main()
    {
        AuthenticationService auth = new AuthenticationService();

        User u = new User()
        {
            email = "email@hotmail.com",
            password = "password123"
        };

        Token newToken = auth.Authenticate(u, ApiUrls.Signin).Result;

        Console.Write("Content: " + newToken.user._id);
        Console.Read();
    }
}
3个回答

37

由于您正在使用 .Result,这将导致代码出现死锁。 这在控制台应用程序中运行的原因是因为控制台应用程序没有上下文,但ASP.NET应用程序有(请参见Stephen Cleary的不要阻塞异步代码)。 您应该将控制器中的Signin方法设置为async,并await调用_authenticationService.Authenticate以解决死锁问题。


23

由于您正在使用 .Result.Waitawait,这将导致代码中的死锁

您可以在 async 方法中使用 ConfigureAwait(false)预防死锁

像这样:

string responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

你可以在任何可能的情况下使用ConfigureAwait(false)来避免异步代码的阻塞。


ConfigureAwait 可以防止死锁。但是它的目的并不只是这个,同时也存在一些副作用(特别是在网站开发中,可能无法访问 HttpContext 中的信息)。在使用 ConfigureAwait 之前最好对其进行更深入的了解。 - CodePB
1
上面引用的这个来源还说:“使用ConfigureAwait(false)来避免死锁是一种危险的做法。您必须对由阻止代码调用的所有方法的传递闭包中的每个await都使用ConfigureAwait(false),包括所有第三方和第二方代码。使用ConfigureAwait(false)来避免死锁,充其量只是一个hack。” - CodePB
本文很好地解释了何时应该使用ConfigureAwait以及需要注意什么。 - CodePB

6

如果有人需要查看代码,我只需将控制器更改为以下内容:

/***
*** Added async and Task<ActionResult>
****/
public async Task<ActionResult> Signin(User user)
{
    //no token needed - we are requesting one
    // added await and remove .Result()
    Token token =  await _authenticationService.Authenticate(user, ApiUrls.Signin);

    return RedirectToAction("Index", "Dashboard", token.user);
}

感谢大家的快速回复!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接