ASP.NET Core中如何用MapDynamicControllerRoute自定义路由选择控制器
ASP.NET实现自定义路由绑定(类似Laravel风格)
问题描述
不想使用[Route("/hello")]路由属性或[controller=hello]/[action=Index]这类常规路由规则,希望通过自定义逻辑直接指定URL对应的控制器动作,例如:
if(context.Request.Path.Value == "/hello"){ SomehowCallTheActionOnController("MyController", "MyAction", anyUrlValuesLikeId); }
尝试过DynamicRouteValueTransformer,但因路由模板捕获了所有路径(得到values["all"]="hello/this/is/my/url"),手动设置控制器名称后无法生效;当前自行实现的代码需要手动处理依赖注入,且ActionRequest可能缺少必要属性,不确定是否规范。
解决方案
ASP.NET完全支持这种自定义路由绑定需求,以下是几种规范实现方式,均由框架处理控制器依赖注入和ActionContext实例化:
方式1:直接端点绑定控制器动作
通过MapGet/MapPost等方法直接将URL绑定到指定控制器的动作,框架自动注入控制器实例:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); // 注册控制器服务 var app = builder.Build(); // 自定义路由规则:将/hello映射到MyController的MyAction方法 app.MapGet("/hello", async (HttpContext context, MyController controller) => { // 获取URL参数(如id) var id = context.Request.Query["id"]; // 调用控制器动作,框架自动处理DI var actionResult = await controller.MyAction(id); // 执行动作结果,框架自动管理ActionContext await actionResult.ExecuteResultAsync(new ActionContext(context, context.GetRouteData(), new ControllerActionDescriptor())); }); app.Run();
方式2:利用DynamicRouteValueTransformer实现全局自定义路由
调整路由模板配合DynamicRouteValueTransformer,让框架根据自定义逻辑匹配控制器动作:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); // 注册自定义路由转换器 builder.Services.AddSingleton<DynamicRouteValueTransformer, MyCustomRouteTransformer>(); var app = builder.Build(); app.UseRouting(); // 匹配所有路径,交由自定义转换器处理 app.MapDynamicControllerRoute<MyCustomRouteTransformer>("{*path}"); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.Run(); // 自定义路由转换器 public class MyCustomRouteTransformer : DynamicRouteValueTransformer { public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext context, RouteValueDictionary values) { var path = values["path"] as string; RouteValueDictionary resultValues = values; // 自定义路由匹配逻辑 if (path == "hello") { resultValues = new RouteValueDictionary { ["controller"] = "MyController", ["action"] = "MyAction", ["id"] = context.Request.Query["id"] // 传递URL参数 }; } else if (path == "user/profile") { resultValues = new RouteValueDictionary { ["controller"] = "UserController", ["action"] = "Profile" }; } return new ValueTask<RouteValueDictionary>(resultValues); } }
方式3:手动添加控制器动作端点
通过ControllerActionEndpointDataSource获取控制器动作的端点描述,手动绑定到指定URL:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var app = builder.Build(); app.UseRouting(); // 获取控制器动作的端点数据源 var dataSource = app.Services.GetRequiredService<ControllerActionEndpointDataSource>(); // 找到目标控制器动作的端点描述 var targetAction = dataSource.Endpoints .OfType<ControllerActionEndpoint>() .First(e => e.ControllerName == "MyController" && e.ActionName == "MyAction"); app.UseEndpoints(endpoints => { // 自定义路由:将/hello映射到目标动作 endpoints.MapGet("/hello", context => { // 设置路由数据,告知框架要调用的控制器和动作 context.GetRouteData().Values["controller"] = "MyController"; context.GetRouteData().Values["action"] = "MyAction"; // 传递URL参数 context.GetRouteData().Values["id"] = context.Request.Query["id"]; // 让框架执行控制器动作,自动处理DI和ActionContext return endpoints.RequestDelegateFactory.Create(targetAction)(context); }); }); app.Run();
总结
以上三种方式都能实现类似Laravel中Route::get('/user', [UserController::class, 'index']);的自定义路由效果,无需手动处理依赖注入或ActionContext的完整初始化,完全遵循ASP.NET的框架规范。
内容的提问来源于stack exchange,提问作者Lake
相关产品推荐
相关产品推荐

