我很乐意在调用内部处理程序的SendAsync()之前执行同步工作,并在内部处理程序完成后执行同步工作。 e.g:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// do some sync work before inner handler here
var sendTask = base.SendAsync(request, cancellationToken);
return sendTask.ContinueWith(
task => { // do some sync work afterwards here });
}
但是,我现在需要从委托处理程序中调用IO绑定操作。 IO绑定操作已经包含为Task<bool>
。我需要使用结果来确定是否继续使用内部处理程序。
一个例子是进行网络调用以授权请求。我必须这样做才能与现有系统集成。一般来说,我认为这个问题有一些有效的方案,它应该有一个可行的解决方案。
在这种情况下,实现SendAsync的正确方法是什么,以便我异步执行IO绑定任务,然后继续异步执行内部处理程序?
关键是我想确定请求线程在任何时候都不会被阻止。
答案 0 :(得分:15)
核心问题是,在获得异步身份验证的结果之前,不能调用内部处理程序SendAsync()。
对我来说,关键的见解是使用TaskCompletionSource(TCS)来控制执行流程。这使我能够从TCS返回任务并在我喜欢的时候在其上设置结果 - 最重要的是延迟调用SendAsync()直到我知道我需要它。
因此,我设置了TCS,然后开始执行授权任务。在此继续,我看看结果。如果被授权,我调用内部处理程序链并附加一个完成TCS的(避免任何线程阻塞)的延续。如果验证失败,我只需在那里完成TCS,然后使用401。
这样做的结果是两个异步任务轮流执行而没有任何线程阻塞。我加载测试了它,似乎工作正常。
尽管使用async / await语法在.NET 4.5中表现得更好......虽然使用TCS的方法基本上仍然在幕后发生,但代码要简单得多。
享受!
第一个片段是在带有Web API测试版的.NET 4.0上构建的 - 这是.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);
}