为什么属性中的 async/await 方法永远不会返回

本文关键字:永远 方法 返回 await 属性 async 为什么 | 更新日期: 2023-09-27 18:01:03

好的,请耐心等待,这可能需要一些解释,因此我有一个简单的帐户控制器;

[RoutePrefix("api/account")]
[Authorize]
[HmacAuthentication]
public class AccountController : ApiController
{
    public async Task<IHttpActionResult> Register(UserModel userModel)
    {
        if (!this.ModelState.IsValid)
        {
            return this.BadRequest(this.ModelState);
        }
        IdentityResult result = await this._userService.RegisterUser(userModel);
        var errorResult = this.GetErrorResult(result);
        if (errorResult != null)
        {
            return errorResult;
        }
        return this.Ok();
    }
}

HmacAuthentication 属性位于此处:

public class HmacAuthenticationAttribute : Attribute, IAuthenticationFilter
{
    public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        ...
        var isValid = this.IsValidRequest(req, appId, incomingBase64Signature, nonce, requestTimeStamp);
        if (isValid.Result)
        {
            ...
        }
        ...
    }
    private async Task<bool> IsValidRequest(
        HttpRequestMessage req, 
        string appId, 
        string incomingBase64Signature, 
        string nonce, 
        string requestTimeStamp)
    {
        ...
        var user = await this.UserService.FindUser(userId); // this never gets a return value
        ...
    }
}

UserService中调用的方法如下:

public async Task<ApplicationUserModel> FindUser(int id)
{
    var user = await this._userBusiness.FindAsync(id);
    return this.MapToModel(user);
}

在商务舱是这样的:

public async Task<ApplicationUser> FindAsync(int id)
{
    var result = await this._userManager.FindByIdAsync(id);
    return result;
}

我面临的问题是,当注册方法称为 HmacAuthentication 属性时,将触发 AuthenticateAsync 筛选器方法。IsValidRequest中查找用户的调用实际上永远不会获得返回值,如果我通过邮递员尝试请求,它永远不会完成。

任何人都可以帮忙吗?

为什么属性中的 async/await 方法永远不会返回

public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        ...
        var isValid = await this.IsValidRequest(req, appId, incomingBase64Signature, nonce, requestTimeStamp);
        if (isValid)
        {
            ...
        }
        ...
    }

正如编译器所建议的那样,您只能在标记为 async 的方法中使用 await 关键字。因此,我相应地更新了AuthenticateAsync的签名。另请注意,您现在可以检查isValid而不是执行isValid.Result因为布尔值将在等待行之后可用。

这可能有点令人困惑,因为 IAuthenticationFilter 的接口没有指定异步(接口不能,它们只能指示该方法将返回 Task(。由实现者决定该方法是只返回任务还是提供异步,以便可以在方法主体中等待值。

接受的

答案在如何解决您的问题方面是正确的,但没有解释为什么会发生这种情况。

这:

var isValid = this.IsValidRequest(
                  req, 
                  appId, 
                  incomingBase64Signature, 
                  nonce, 
                  requestTimeStamp);
if (isValid.Result)

导致代码死锁。您正在同步阻止异步方法。发生这种情况是因为当编译器看到async方法时,它会生成一个状态机,该状态机获取第一次await后的所有内容并将其包装为延续。编译器还会查看是否涉及任何同步上下文,如果有,它会尝试将延续封送回AspNetSynchronizationContext,这被您的.Result调用阻止,从而有效地导致死锁。

这就是为什么你不应该阻止异步代码