ASP.NET Core:IApplicationModelConvention添加ControllerModel后路由无法识别
解决动态添加Controller无法被路由识别的坑
之前我折腾动态添加控制器的时候踩了个坑:因为控制器的动作也是动态生成的,没法直接用泛型控制器,查了文档后觉得重写IApplicationModelConvention的Apply方法是最优方案,但添加的ControllerModel始终不被路由系统识别。先给大家看看我最初的实现代码:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(opts => { opts.Conventions.Add(new ApplicationDescription("mdesc")); opts.Conventions.Add(new ControllerDescription("mctrl")); }) // 其他配置代码... } public class ApplicationDescription : IApplicationModelConvention { private readonly string _description; public ApplicationDescription(string description) { _description = description; } public void Apply(ApplicationModel application) { var basec = application.Controllers[0]; var cm = new ControllerModel(typeof(MyController).GetTypeInfo(), basec.Attributes); application.Controllers.Add(cm); application.Properties["description"] = _description; } }
后来才搞明白,问题出在创建ControllerModel的时候太草率了——只复制了基础控制器的属性,但没给动态控制器补全路由匹配必需的元数据,路由系统根本找不到它。下面是修正后的代码,我把关键的补全步骤都加上了:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(opts => { opts.Conventions.Add(new ApplicationDescription("mdesc")); opts.Conventions.Add(new ControllerDescription("mctrl")); }) // 其他配置... } public class ApplicationDescription : IApplicationModelConvention { private readonly string _description; public ApplicationDescription(string description) { _description = description; } public void Apply(ApplicationModel application) { var baseController = application.Controllers[0]; var controllerTypeInfo = typeof(MyController).GetTypeInfo(); // 1. 完整初始化ControllerModel,传入特性数组 var dynamicController = new ControllerModel(controllerTypeInfo, baseController.Attributes.ToArray()) { // 2. 设置控制器名称(必须去掉Controller后缀,路由系统靠这个匹配) ControllerName = controllerTypeInfo.Name.Replace("Controller", string.Empty), // 3. 复制基础控制器的路由值,保证和普通控制器行为一致 RouteValues = new Dictionary<string, string>(baseController.RouteValues), // 4. 添加选择器,这是路由能找到控制器的核心 Selectors = new List<SelectorModel>(baseController.Selectors) }; // 5. 给控制器添加动作模型——没有动作的控制器无法响应请求 // 如果你的动作是动态生成的,可以在这里通过自定义逻辑生成ActionModel var indexAction = new ActionModel(typeof(MyController).GetMethod("Index"), new Attribute[0]) { ActionName = "Index", Selectors = new List<SelectorModel> { new SelectorModel { AttributeRouteModel = new AttributeRouteModel { Template = $"{dynamicController.ControllerName}/Index" } } } }; dynamicController.Actions.Add(indexAction); // 6. 把配置好的动态控制器加入应用模型 application.Controllers.Add(dynamicController); application.Properties["description"] = _description; } }
这里几个关键的修复点必须注意:
- 必须设置
ControllerName:路由系统依赖这个名称生成路由规则,一定要把类名的"Controller"后缀去掉,比如MyController要设成"My"。 - 初始化
Selectors选择器:选择器是路由匹配的核心标识,没有它路由系统完全不知道这个控制器的存在。 - 添加
ActionModel:控制器至少要有一个可访问的动作,否则就算被识别也无法处理请求。如果你的动作是完全动态的,可以通过反射或者自定义逻辑生成对应的动作模型。 - 复制必要元数据:比如路由值、控制器特性这些,保证动态控制器和手动编写的控制器有一致的行为。
内容的提问来源于stack exchange,提问作者WoF_Angel
相关产品推荐
相关产品推荐

