C# 任务ContinueWith未返回预期值
这可能是RTFM类型的问题之一,但是,我一辈子都搞不清楚如何以一种适用于Asp.net WebApi的方式将任务链接在一起 具体来说,我正在研究如何在控制器处理响应后使用C# 任务ContinueWith未返回预期值,c#,.net,asp.net-web-api,task-parallel-library,async-await,C#,.net,Asp.net Web Api,Task Parallel Library,Async Await,这可能是RTFM类型的问题之一,但是,我一辈子都搞不清楚如何以一种适用于Asp.net WebApi的方式将任务链接在一起 具体来说,我正在研究如何在控制器处理响应后使用DelegatingHandler修改响应(添加额外的头),并尝试使用HttpMessageInvoker实例对DelegatingHandler进行单元测试 第一模式 此操作在continueTask.Start()上失败,但System.InvalidOperationException除外:不能在继续任务上调用Start。
DelegatingHandler
修改响应(添加额外的头),并尝试使用HttpMessageInvoker
实例对DelegatingHandler
进行单元测试
第一模式
此操作在continueTask.Start()
上失败,但System.InvalidOperationException除外:不能在继续任务上调用Start。
第三种模式
如果我将生产代码更改为返回延续任务,而不是从base.SendAsync
返回结果,那么我将得到关于调用延续任务的Start
的第二个单元测试异常
我想我想在我的生产代码中完成第三个单元测试模式,但是,我不知道如何编写它
如何执行我想要的操作(在调用断言之前添加头)?返回映射的任务,因此您需要返回该任务:
var task = base.SendAsync(request, cancellationToken);
return task.ContinueWith(AddWwwAuthenticateHeaderTask());
返回
任务
时,它应该始终是热任务
,这意味着返回的任务已经启动。让某人对返回的任务显式调用Start()
,这会让人感到困惑,并且违反了指导原则
要正确查看延续结果,请执行以下操作:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith(AddWwwAuthenticateHeaderTask()).Unwrap();
}
受保护的覆盖任务SendAsync(
HttpRequestMessage请求,CancellationToken CancellationToken)
{
返回base.sendaync(请求,取消令牌).ContinueWith(AddWwwAuthenticateHeaderTask()).Unwrap();
}
您应该修改base.SendAsync
以返回已启动的任务
从:
从TAP方法返回的所有任务都必须是“热的”。如果TAP方法在内部使用任务的构造函数来实例化要返回的任务,则TAP方法必须在返回任务对象之前对其调用Start。TAP方法的使用者可以安全地假设返回的任务是“热的”,并且不应该尝试调用从TAP方法返回的任何任务的Start。对“热”任务调用Start将导致InvalidOperationException(该检查由task类自动处理)
尝试以下方法。注意
task2.Unwrap()
,我认为其他答案没有提到这一部分:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var task1 = base.SendAsync(request, cancellationToken);
var task2 = task1.ContinueWith(t => AddWwwAuthenticateHeaderTask(),
cancellationToken);
return task2.Unwrap();
}
无论哪种情况,都不会在原始同步上下文(可能是aspnetsynchronizationcontext
)上继续。如果需要保持相同的上下文,请使用TaskScheduler.FromCurrentSynchronizationContext()
而不是TaskScheduler.Default
。警告一句:在ASP.NET中
我一辈子都搞不清楚如何以一种适用于Asp.net WebApi的方式将任务链接在一起
接受async
和wait
。特别是,将ContinueWith
替换为wait
(不要使用任务构造函数或Start
):
受保护的覆盖异步任务SendAsync(
HttpRequestMessage请求,CancellationToken CancellationToken)
{
var response=await base.sendaync(请求、取消令牌);
if(response.StatusCode==HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(
新的AuthenticationHeaderValue(“基本”、“领域=\“api\”);
}
返回响应;
}
您使用的是哪个.NET framework?即使任务已经完成,也会运行Continuations。你认为这不是导致你过度谨慎的开始行为的原因。没有必要。谢谢你提供的指导意见,看起来我是在违反规则。原来我的问题是我的DelegatingHttpMessageHandler
类,我没有开始它创建的任务。
public class BasicAuthenticationHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var task = base.SendAsync(request, cancellationToken);
task.ContinueWith(AddWwwAuthenticateHeaderTask());
return task;
}
private static Func<Task<HttpResponseMessage>, HttpResponseMessage>
AddWwwAuthenticateHeaderTask()
{
return task =>
{
var response = task.Result;
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue("Basic", "realm=\"api\""));
}
return response;
};
}
}
[TestMethod]
public void should_give_WWWAuthenticate_header_if_authentication_is_missing()
{
using (var sut = new BasicAuthenticationHandler())
{
sut.InnerHandler = new DelegatingHttpMessageHandler(
() => new HttpResponseMessage(HttpStatusCode.Unauthorized));
using (var invoker = new HttpMessageInvoker(sut))
{
var task = invoker.SendAsync(_requestMessage, CancellationToken.None);
task.Start();
Assert.IsTrue(
task.Result.Headers.WwwAuthenticate.Contains(
new AuthenticationHeaderValue("Basic", "realm=\"api\"")));
}
}
}
var task = base.SendAsync(request, cancellationToken);
return task.ContinueWith(AddWwwAuthenticateHeaderTask());
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken).ContinueWith(AddWwwAuthenticateHeaderTask()).Unwrap();
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var task1 = base.SendAsync(request, cancellationToken);
var task2 = task1.ContinueWith(t => AddWwwAuthenticateHeaderTask(),
cancellationToken);
return task2.Unwrap();
}
var task2 = task1.ContinueWith(
t => AddWwwAuthenticateHeaderTask(),
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue("Basic", "realm=\"api\""));
}
return response;
}