自定义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/index | System.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); } }
修改说明
- RouteData初始化:创建
RouteData时,必须将当前自定义IRouter和默认处理程序加入Routers集合,确保ActionContext能关联到有效的路由实例。 - 实现GetVirtualPath:将路径生成逻辑委托给默认处理程序,保证
Url.Action能正确解析并生成目标链接。
修改后,访问/test时,Url.Action("Index", "Home")会正常返回/home/index。
内容的提问来源于stack exchange,提问作者Oskar Sjöberg
相关产品推荐
相关产品推荐

