如何从config.json为ASP.NET Core控制器所有Action设置路由前缀?
你的问题出在混用了特性路由和传统路由体系,这两套机制在ASP.NET Core中是独立运行的,不会自动合并,所以你用routes.MapRoute配置的传统路由前缀无法和控制器/Action上的[Route]特性路径结合,导致最终路由丢失了前缀。
下面给你两种可行的解决方案,推荐第二种,更符合特性路由的使用习惯:
方案一:使用全局路由前缀约定(推荐)
这种方法可以给所有带有[Route]特性的控制器统一添加来自配置的前缀,步骤如下:
1. 在config.json中添加前缀配置
{ "ApiSettings": { "ControllerPrefix": "api/version/v1" } }
2. 实现自定义路由约定类
创建一个实现IControllerModelConvention的类,用来给控制器路由添加全局前缀:
public class RoutePrefixConvention : IControllerModelConvention { private readonly AttributeRouteModel _globalPrefix; public RoutePrefixConvention(string prefix) { _globalPrefix = new AttributeRouteModel(new RouteAttribute(prefix)); } public void Apply(ControllerModel controller) { // 遍历控制器的所有路由选择器,合并全局前缀 foreach (var selector in controller.Selectors) { if (selector.AttributeRouteModel != null) { // 合并全局前缀和控制器/Action的路由 selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel( _globalPrefix, selector.AttributeRouteModel); } else { // 如果控制器没有设置[Route]特性,直接使用全局前缀 selector.AttributeRouteModel = _globalPrefix; } } } }
3. 在Startup.cs中注册约定并绑定配置
在ConfigureServices方法里,读取配置并添加自定义约定:
public void ConfigureServices(IServiceCollection services) { // 读取配置中的API前缀 var apiPrefix = Configuration.GetValue<string>("ApiSettings:ControllerPrefix"); services.AddControllers(options => { // 添加全局路由前缀约定 options.Conventions.Add(new RoutePrefixConvention(apiPrefix)); }); }
4. 简化控制器的Route特性配置
现在你的控制器可以去掉原来的固定前缀[Route],Action直接保留自己的路由特性即可:
// 控制器不需要再写固定前缀的[Route]特性 public class MyController : ControllerBase { [Route("receipts/verifyReceipt")] public IActionResult VerifyReceipt(...){....} // 其他Action的Route特性保持不变 }
这样生成的路由就是你想要的api/version/v1/receipts/verifyReceipt。
方案二:针对单个控制器绑定配置前缀
如果你只想给特定控制器添加配置的前缀,而不是全局所有控制器,可以这样做:
1. 同样先在config.json中添加配置(同方案一)
2. 在Startup.cs中绑定配置类
先创建配置类:
public class ApiSettings { public string ControllerPrefix { get; set; } }
然后在ConfigureServices中绑定:
services.Configure<ApiSettings>(Configuration.GetSection("ApiSettings"));
3. 使用约定给单个控制器添加前缀
在ConfigureServices中,针对特定控制器添加前缀:
services.AddControllers(options => { var apiPrefix = Configuration.GetValue<string>("ApiSettings:ControllerPrefix"); // 只给MyController添加前缀 options.Conventions.Add(new RoutePrefixConvention(apiPrefix) .ForController<MyController>()); });
(注:如果你的RoutePrefixConvention没有ForController方法,可以扩展一下,或者直接在Apply方法里判断控制器类型)
为什么你原来的配置无效?
你用routes.MapRoute配置的是传统路由,而控制器上的[Route]属于特性路由,ASP.NET Core会优先使用特性路由,忽略传统路由的配置。当你给控制器设置[Route("", Name = "default")]时,相当于控制器没有路由前缀,所以Action的路由就直接是/receipts/verifyReceipt,自然丢失了前缀。
内容的提问来源于stack exchange,提问作者user1409531




