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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:44:43