C# DelegatingHandler设置CurrentPrincipal
我试图对DelegateHandler的一个实现进行单元测试。我的简化实施:C# DelegatingHandler设置CurrentPrincipal,c#,unit-testing,asp.net-web-api,mstest,C#,Unit Testing,Asp.net Web Api,Mstest,我试图对DelegateHandler的一个实现进行单元测试。我的简化实施: public class FooHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { Th
public class FooHandler
: DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
Thread.CurrentPrincipal = new GenericPrincipal(
new GenericIdentity("Vegard"), new[] { "A", "B" });
return await base.SendAsync(request, cancellationToken);
}
}
公共类foodhandler
:DelegatingHandler
{
受保护的覆盖异步任务SendAsync(
HttpRequestMessage请求,CancellationToken CancellationToken)
{
Thread.CurrentPrincipal=新的GenericPrincipal(
新的通用实体(“Vegard”)、新的[]{“A”、“B”});
返回wait base.sendaync(请求、取消令牌);
}
}
当我尝试进行单元测试时,我是这样做的:
public class TestHandler : DelegatingHandler
{
private readonly Func<HttpRequestMessage,
CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public TestHandler()
{
_handlerFunc = (r, c) => Return(HttpStatusCode.OK);
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
return _handlerFunc(request, cancellationToken);
}
public static Task<HttpResponseMessage> Return(HttpStatusCode status)
{
return Task.Factory.StartNew(
() => new HttpResponseMessage(status));
}
}
[TestMethod]
public async Task SendAsync_CorrectTokens_IsAuthorized()
{
var message = new HttpRequestMessage(HttpMethod.Get, "http://www.test.com");
var handler = new AuthorizationHeaderHandler
{
InnerHandler = new TestHandler()
};
var invoker = new HttpMessageInvoker(handler);
var result = await invoker.SendAsync(message, new CancellationToken());
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.IsTrue(Thread.CurrentPrincipal.Identity.IsAuthenticated); // fails
Assert.AreEqual("Vegard", Thread.CurrentPrincipal.Identity.Name); // fails
}
公共类TestHandler:DelegatingHandler
{
私有只读函数_handlerFunc;
公共TestHandler()
{
_handlerFunc=(r,c)=>Return(HttpStatusCode.OK);
}
受保护的覆盖任务SendAsync(
HttpRequestMessage请求,CancellationToken CancellationToken)
{
返回_handlerFunc(请求、取消令牌);
}
公共静态任务返回(HttpStatusCode状态)
{
return Task.Factory.StartNew(
()=>新的HttpResponseMessage(状态));
}
}
[测试方法]
公共异步任务SendAsync\u CorrectTokens\u已授权()
{
var message=新的HttpRequestMessage(HttpMethod.Get)http://www.test.com");
var handler=新授权HeaderHandler
{
InnerHandler=新的TestHandler()
};
var invoker=newhttpmessageinvoker(处理程序);
var result=wait invoker.sendaync(消息,new CancellationToken());
AreEqual(HttpStatusCode.OK,result.StatusCode);
Assert.IsTrue(Thread.CurrentPrincipal.Identity.IsAuthenticated);//失败
Assert.AreEqual(“Vegard”,Thread.CurrentPrincipal.Identity.Name);//失败
}
我猜这是因为
HttpMessageInvoker
在单独的线程上运行DelegateHandler
。我可以强制它们在同一个线程上吗?您实际遇到的是wait的行为。等待将在退出等待时将主体重置为您进入等待时的状态。因此,由于在调用Wait invoker.SendAsync时没有当前主体,因此在等待该调用后将没有当前主体
但是,您的测试处理程序应该看到正确的主体。您可以做的是让您的测试处理程序在其SendAsync实现中存储当前主体,将其作为公共属性公开,然后让您的测试断言测试处理程序看到了它应该看到的主体。这应该很好,这应该是你关心的行为
我能强迫它们在同一条线上吗
你不能
更好的问题是“如何将Thread.CurrentPrincipal
流到执行请求的任何线程”?这个问题有答案
Thread.CurrentPrincipal
在ASP.NET中是奇数。事实上,我建议你根本不要使用它;改用HttpContext.User
。但如果您愿意,您可以通过了解以下几点来实现:
HttpContext.User
由ASP.NETSynchronizationContext
流式传输HttpContext.User
就会覆盖Thread.CurrentPrincipal
- 请求完成后,
的值未定义Thread.CurrentPrincipal
- 按照当前运行测试的方式,没有
(或ASP.NETHttpContext
),这会干扰主要用户的流动SynchronizationContext