【问题标题】:DelegatingHandler setting CurrentPrincipalDelegatingHandler 设置 CurrentPrincipal
【发布时间】:2013-06-10 12:18:10
【问题描述】:

我正在尝试对 DelegateHandler 的实现进行单元测试。我的简化实现:

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);
    }
}

当我尝试对此进行单元测试时,我会这样做:

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
}

我猜这是因为HttpMessageInvoker 在单独的线程上运行DelegateHandler。我可以强制它们在同一个线程上吗?

【问题讨论】:

    标签: c# unit-testing asp.net-web-api mstest


    【解决方案1】:

    我可以强制这些在同一个线程上吗?

    你不能。

    一个更好的问题是“我如何将Thread.CurrentPrincipal 流向执行请求的任何线程”? 这个问题的答案。

    Thread.CurrentPrincipal 在 ASP.NET 中是奇怪的。事实上,我建议你根本不要使用它;请改用HttpContext.User。但是,如果您愿意,可以通过了解以下几点来使其发挥作用:

    1. HttpContext.User 由 ASP.NET SynchronizationContext 流转。
    2. 每当线程进入 ASP.NET 请求 SynchronizationContext 时,Thread.CurrentPrincipal 就会被 HttpContext.User 覆盖。

    很遗憾,您当前的测试在几个关键点上存在缺陷:

    • 请求完成后,Thread.CurrentPrincipal 的值未定义。
    • 当前运行测试的方式,没有HttpContext(或 ASP.NET SynchronizationContext),这会干扰主要用户的流动。

    要全面测试授权,您需要进行集成测试。

    另见my answer to this question

    【讨论】:

      【解决方案2】:

      您实际遇到的是等待的行为。当您退出等待时,等待会将主体重置为您进入等待时的状态。因此,由于在您调用 await invoker.SendAsync 时没有当前委托人,所以在您等待该调用后将没有当前委托人。

      但是,您的测试处理程序应该看到正确的主体。您可以做的是让您的测试处理程序将当前主体存储在其 SendAsync 实现中,将其公开为公共属性,然后让您的测试断言测试处理程序看到了它应该看到的主体。这应该可以正常工作,并且应该是您关心的行为。

      【讨论】:

        猜你喜欢
        • 2011-06-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-06
        • 1970-01-01
        • 1970-01-01
        • 2013-09-10
        • 1970-01-01
        相关资源
        最近更新 更多