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

自定义IRouter路由时UrlHelper.Action抛出异常问题咨询

自定义IRouter场景下Url.Action抛出InvalidOperationException的解决方法

问题描述

将中型遗留ASP.NET MVC应用迁移至ASP.NET Core 7.0,系统使用自定义IRouter解析请求对应的控制器与动作。通过常规路由访问控制器时,Url.Action可正常生成链接;但通过自定义IRouter调用同一控制器动作时,Url.Action抛出以下异常:

System.InvalidOperationException:
Could not find an IRouter associated with the ActionContext. If your application is 
using endpoint routing then you can get a IUrlHelperFactory with dependency injection 
and use it to create a UrlHelper, or use Microsoft.AspNetCore.Routing.LinkGenerator.

复现代码

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMvc(options => { options.EnableEndpointRouting = false; });
var app = builder.Build();

app.UseMvc(routes =>
{
    routes.MapRoute(null, "home/index", new { controller = "Home", action = "Index" });
    routes.Routes.Add(new CustomRouter(routes.DefaultHandler));                         
});

app.Run();

public class HomeController : Controller
{
    public string Index() => Url.Action("Index", "Home");
}

public class CustomRouter : IRouter
{
    private readonly IRouter defaultHandler;

    public CustomRouter(IRouter defaultHandler)
    {
        this.defaultHandler = defaultHandler;
    }

    public async Task RouteAsync(RouteContext context)
    {
        if (context.HttpContext.Request.Path != "/test")
        {
            return;
        }

        var dictionary = new RouteValueDictionary
        {
             { "controller", "Home" }, 
             { "action", "Index" }
        };

        context.RouteData = new RouteData(dictionary);
        await defaultHandler.RouteAsync(context);
    }

    public VirtualPathData? GetVirtualPath(VirtualPathContext context) => null;
}

测试结果

Url预期输出实际输出成功
/home/index/home/index/home/index是
/test/home/indexSystem.InvalidOperationException否

解决方案

问题根源在于自定义IRouter处理请求时,未将自身添加到RouteData.Routers集合中,导致Url.Action生成链接时无法找到关联的路由实例;同时GetVirtualPath方法返回null,无法完成路径生成。

修改CustomRouter类如下:

public class CustomRouter : IRouter
{
    private readonly IRouter defaultHandler;

    public CustomRouter(IRouter defaultHandler)
    {
        this.defaultHandler = defaultHandler;
    }

    public async Task RouteAsync(RouteContext context)
    {
        if (context.HttpContext.Request.Path != "/test")
        {
            return;
        }

        var dictionary = new RouteValueDictionary
        {
             { "controller", "Home" }, 
             { "action", "Index" }
        };

        // 初始化RouteData并添加当前路由与默认处理程序到路由集合
        context.RouteData = new RouteData();
        context.RouteData.Routers.Add(this);
        context.RouteData.Routers.Add(defaultHandler);
        
        // 添加路由值
        foreach (var kvp in dictionary)
        {
            context.RouteData.Values.Add(kvp.Key, kvp.Value);
        }

        await defaultHandler.RouteAsync(context);
    }

    public VirtualPathData? GetVirtualPath(VirtualPathContext context)
    {
        // 委托默认处理程序生成虚拟路径
        return defaultHandler.GetVirtualPath(context);
    }
}

修改说明

  1. RouteData初始化:创建RouteData时,必须将当前自定义IRouter和默认处理程序加入Routers集合,确保ActionContext能关联到有效的路由实例。
  2. 实现GetVirtualPath:将路径生成逻辑委托给默认处理程序,保证Url.Action能正确解析并生成目标链接。

修改后,访问/test时,Url.Action("Index", "Home")会正常返回/home/index。

内容的提问来源于stack exchange,提问作者Oskar Sjöberg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 19:05:32