为什么 async/await 属性中的方法从不 return
Why does async/await method in attribute never return
好的,请耐心等待,这可能需要一些解释,因此我有一个简单的帐户控制器;
[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);
}
在业务中 class 是这样的:
public async Task<ApplicationUser> FindAsync(int id)
{
var result = await this._userManager.FindByIdAsync(id);
return result;
}
我面临的问题是,当调用 Register 方法时,会触发 HmacAuthentication 属性并执行 AuthenticateAsync 过滤器方法。 IsValidRequest
中查找用户的调用实际上从未获得 return 值,如果我尝试通过邮递员发出请求,它永远不会完成。
有人可以帮忙吗?
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 的接口没有指定异步(接口不能,它们只能指示该方法将 return 一个任务)。由实现者决定该方法是简单地 return 任务还是提供异步以便可以在方法主体内等待值。
接受的答案在如何解决您的问题方面是正确的,但没有解释为什么会发生这种情况。
这个:
var isValid = this.IsValidRequest(
req,
appId,
incomingBase64Signature,
nonce,
requestTimeStamp);
if (isValid.Result)
导致您的代码死锁。您正在异步方法上同步阻塞。发生这种情况是因为当编译器看到 async
方法时,它会生成一个状态机,该状态机在您的第一个 await
之后获取所有内容并将其包装为延续。编译器还会查看是否涉及任何同步上下文,如果有,它会尝试将延续编组回 AspNetSynchronizationContext
,这会被您的 .Result
调用阻塞,从而有效地导致死锁。
好的,请耐心等待,这可能需要一些解释,因此我有一个简单的帐户控制器;
[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);
}
在业务中 class 是这样的:
public async Task<ApplicationUser> FindAsync(int id)
{
var result = await this._userManager.FindByIdAsync(id);
return result;
}
我面临的问题是,当调用 Register 方法时,会触发 HmacAuthentication 属性并执行 AuthenticateAsync 过滤器方法。 IsValidRequest
中查找用户的调用实际上从未获得 return 值,如果我尝试通过邮递员发出请求,它永远不会完成。
有人可以帮忙吗?
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 的接口没有指定异步(接口不能,它们只能指示该方法将 return 一个任务)。由实现者决定该方法是简单地 return 任务还是提供异步以便可以在方法主体内等待值。
接受的答案在如何解决您的问题方面是正确的,但没有解释为什么会发生这种情况。
这个:
var isValid = this.IsValidRequest(
req,
appId,
incomingBase64Signature,
nonce,
requestTimeStamp);
if (isValid.Result)
导致您的代码死锁。您正在异步方法上同步阻塞。发生这种情况是因为当编译器看到 async
方法时,它会生成一个状态机,该状态机在您的第一个 await
之后获取所有内容并将其包装为延续。编译器还会查看是否涉及任何同步上下文,如果有,它会尝试将延续编组回 AspNetSynchronizationContext
,这会被您的 .Result
调用阻塞,从而有效地导致死锁。