You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在AuthorizationHandler单元测试中模拟RouteData?

ASP.NET Core 授权处理程序单元测试中读取路由数据的问题

我在Web API项目里有一个授权处理程序(AuthorizationHandler),运行时能正常读取路由数据(非查询字符串),但单元测试时怎么都搞不定,试了好几种HttpContext模拟方式都达不到预期效果。

补充说明:测试时希望httpContext.GetRouteValue("param")返回"myparamvalue",就像实际调用localhost:8080/api/controller/myparamvalue/my-action端点时的结果一样。


API端点代码

[Authorize(Policy = "MyCustomPolicy")]
[Route("{param}/my-action")]
[HttpGet]
public ActionResult<string> GetResult([FromRoute] string param)
{
    return Content("Dummy response");
}

授权处理程序代码

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement)
{
    // 其他代码
    string paramValue = string.Empty;
    if (context.Resource is HttpContext httpContext)
    {
        paramValue = Convert.ToString(httpContext.GetRouteValue("param"));
    }
    // 使用paramValue的后续代码
}

当前单元测试代码

public async Task SucceedWhenCalledWithParam()
{
    //Arrange
    ClaimsPrincipal principal = CreatePrincipal("testscope", "scope");
    var requirement = new MyRequirement();

    var httpContext = new DefaultHttpContext();
    httpContext.Request.Host = new HostString("localhost:8080");
    httpContext.Request.Path = new PathString("/api/controller/myparamvalue/my-action");

    AuthorizationHandlerContext context = new(new[] { requirement }, principal, httpContext);

    MyAuthHandler authHandler = new();

    //Act
    await authHandler.HandleAsync(context).ConfigureAwait(false);

    //Assert
    Assert.IsTrue(context.HasSucceeded);
}

解决方案

问题根源:直接设置Request.Path不会自动解析路由数据,GetRouteValue读取的是HttpContext.Request.RouteValues集合中的数据,这个集合需要手动填充或通过路由匹配器生成。

方案1:手动添加路由值(简单直接)

修改单元测试的Arrange部分,直接给RouteValues添加目标参数:

public async Task SucceedWhenCalledWithParam()
{
    //Arrange
    ClaimsPrincipal principal = CreatePrincipal("testscope", "scope");
    var requirement = new MyRequirement();

    var httpContext = new DefaultHttpContext();
    // 手动添加路由参数值
    httpContext.Request.RouteValues.Add("param", "myparamvalue");

    AuthorizationHandlerContext context = new(new[] { requirement }, principal, httpContext);

    MyAuthHandler authHandler = new();

    //Act
    await authHandler.HandleAsync(context).ConfigureAwait(false);

    //Assert
    Assert.IsTrue(context.HasSucceeded);
}

方案2:模拟完整路由匹配(更贴近真实场景)

如果需要验证路由模板与请求路径的对应关系,可以使用路由匹配器模拟真实解析过程:

public async Task SucceedWhenCalledWithParam()
{
    //Arrange
    ClaimsPrincipal principal = CreatePrincipal("testscope", "scope");
    var requirement = new MyRequirement();

    var httpContext = new DefaultHttpContext();
    httpContext.Request.Path = new PathString("/api/controller/myparamvalue/my-action");

    // 配置对应路由模板
    var routeOptions = new RouteOptions();
    routeOptions.Routes.Add(new Route(
        name: "TestRoute",
        template: "/api/controller/{param}/my-action",
        defaults: new RouteValueDictionary(),
        constraints: new RouteValueDictionary(),
        handler: null
    ));

    // 创建匹配器并执行路由匹配
    var matcherFactory = new DefaultMatcherFactory();
    var matcher = matcherFactory.CreateMatcher(routeOptions);
    var matchResult = matcher.Match(httpContext);

    // 将匹配得到的路由值赋值给HttpContext
    foreach (var kvp in matchResult.Values)
    {
        httpContext.Request.RouteValues.Add(kvp.Key, kvp.Value);
    }

    AuthorizationHandlerContext context = new(new[] { requirement }, principal, httpContext);

    MyAuthHandler authHandler = new();

    //Act
    await authHandler.HandleAsync(context).ConfigureAwait(false);

    //Assert
    Assert.IsTrue(context.HasSucceeded);
}

内容的提问来源于stack exchange,提问作者Vinod

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 13:55:14