好的,我想我已经破解了。我用一个身份验证场景来说明这一点:我想异步地对用户进行身份验证,并使用结果来决定是返回 401 还是继续使用消息处理程序链。
中心问题是,在获得异步身份验证的结果之前,您不能调用内部处理程序 SendAsync()。
对我来说,关键的见解是使用 TaskCompletionSource (TCS) 来控制执行流程。这使我能够从 TCS 返回任务并在我喜欢的时候在其上设置结果 - 最重要的是延迟调用 SendAsync() 直到我知道我需要它。
因此,我设置了 TCS,然后启动了一项任务来进行授权。在继续这个,我看看结果。如果获得授权,我将调用内部处理程序链并附加一个继续(避免任何线程阻塞)以完成 TCS。如果身份验证失败,我只需在那里完成 TCS,然后使用 401。
这样做的结果是两个异步任务轮流执行,没有任何线程阻塞。我对此进行了负载测试,它似乎工作得很好。
不过,在 .NET 4.5 中,使用 async/await 语法的情况要好得多……虽然 TCS 的方法仍然基本上是在幕后进行的,但代码要简单得多。
享受!
第一个片段基于 .NET 4.0 和 Web API Beta - 第二个片段基于 .NET 4.5/Web API RC。
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
// Authorize() returns a started
// task that authenticates the user
// if the result is false we should
// return a 401 immediately
// otherwise we can invoke the inner handler
Task<bool> authenticationTask = Authorize(request);
// attach a continuation...
authenticationTask.ContinueWith(_ =>
{
if (authenticationTask.Result)
{
// authentication succeeded
// so start the inner handler chain
// and write the result to the
// task completion source when done
base.SendAsync(request, cancellationToken)
.ContinueWith(t => taskCompletionSource.SetResult(t.Result));
}
else
{
// authentication failed
// so complete the TCS immediately
taskCompletionSource.SetResult(
new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
});
return taskCompletionSource.Task;
}
这是一个 .NET 4.5 / Web API Release Candidate 版本,它使用新的 async/await 语法更加性感:
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Authorize still has a Task<bool> return type
// but await allows this nicer inline syntax
var authorized = await Authorize(request);
if (!authorized)
{
return new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
Content = new StringContent("Unauthorized.")
};
}
return await base.SendAsync(request, cancellationToken);
}