使用AddApplicationPart注册控制器时如何配置Area区域?
实现无[Area]特性的控制器区域配置
要实现无需在控制器上标记[Area]特性即可灵活配置所属区域(包括多区域),可以通过**自定义控制器模型约定(IControllerModelConvention)**结合配置驱动的方式来实现,以下是具体步骤:
1. 定义配置结构
首先在appsettings.json中添加模块的区域-控制器映射配置,支持一个控制器关联多个区域:
"Modules": { "MyModule": { "ApplicationParts": ["MyModule.Controllers.dll"], "AreaMappings": [ { "AreaName": "MyArea", "ControllerNames": ["UserController"] }, { "AreaName": "AnotherArea", "ControllerNames": ["UserController"] } ] } }
然后创建对应的配置模型类,用于绑定配置:
public class AreaMappingConfig { public string AreaName { get; set; } public List<string> ControllerNames { get; set; } = new(); } public class MyModuleConfig { public List<string> ApplicationParts { get; set; } = new(); public List<AreaMappingConfig> AreaMappings { get; set; } = new(); }
2. 自定义控制器模型约定
实现IControllerModelConvention,在控制器初始化时根据配置自动添加区域元数据(模拟[Area]特性的作用):
public class ConfigurableAreaConvention : IControllerModelConvention { private readonly List<AreaMappingConfig> _areaMappings; public ConfigurableAreaConvention(List<AreaMappingConfig> areaMappings) { _areaMappings = areaMappings; } public void Apply(ControllerModel controller) { foreach (var mapping in _areaMappings) { if (mapping.ControllerNames.Contains(controller.ControllerName)) { // 为匹配的控制器添加Area特性,支持多区域关联 controller.Attributes.Add(new AreaAttribute(mapping.AreaName)); } } } }
3. 修改模块注册逻辑
更新MyModule的RegisterModule方法,读取配置并注入自定义约定:
public class MyModule : IModule { public IServiceCollection RegisterModule(IServiceCollection services) { var configuration = AppSettingsHelper.Configuration; if (configuration == null) return services; // 绑定模块配置 var moduleConfig = configuration.GetSection("Modules:MyModule").Get<MyModuleConfig>(); if (moduleConfig == null) return services; foreach (var assemblyFileName in moduleConfig.ApplicationParts) { var moduleAssembly = AppDomain.CurrentDomain .GetAssemblies() .FirstOrDefault(a => a.FullName == Path.GetFileName(assemblyFileName)); if (moduleAssembly == null && !string.IsNullOrEmpty(assemblyFileName)) { moduleAssembly = Assembly.LoadFrom(assemblyFileName); } if (moduleAssembly != null) { services.AddMvcCore() .AddApplicationPart(moduleAssembly) .AddControllersAsServices() .AddMvcOptions(options => { // 添加自定义区域约定 options.Conventions.Add(new ConfigurableAreaConvention(moduleConfig.AreaMappings)); }); } } return services; } public IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder endpoints) { var moduleConfig = AppSettingsHelper.Configuration.GetSection("Modules:MyModule").Get<MyModuleConfig>(); if (moduleConfig != null) { // 为每个配置的区域注册路由 foreach (var areaMapping in moduleConfig.AreaMappings) { endpoints.MapAreaControllerRoute( areaName: areaMapping.AreaName, name: areaMapping.AreaName, pattern: $"{areaMapping.AreaName}/{{controller}}/{{action}}" ); } } return endpoints; } }
4. 简化控制器代码
移除控制器上的[Area]特性,调整路由模板让区域前缀自动拼接:
[ApiController] [Route("{controller}")] public class UserController : ControllerBase { [HttpGet("{id}")] public IActionResult GetById([FromRoute]int id, [FromBody] UserMode user) { return Ok(); } [HttpGet] public IActionResult GetAll() { return Ok(); } }
关键说明
- 该方案通过配置文件灵活控制控制器与区域的关联,无需修改控制器代码
- 支持单个控制器关联多个区域,每个区域对应独立的路由访问路径(如
MyArea/User/GetById和AnotherArea/User/GetById) - 自定义约定完全贴合ASP.NET Core的路由机制,无需额外修改路由核心逻辑
内容的提问来源于stack exchange,提问作者Colin
相关产品推荐
相关产品推荐

