如何在ASP.NET Core 7中条件化添加指定控制器?
多集群多租户场景下按需发布控制器端点的实现方案
针对你这种同一应用部署在多集群、各集群服务特定租户,需要仅在指定集群发布特定控制器的场景,这里提供几个比路由内判断返回404更简洁的方案:
方案一:基于配置的控制器条件注册(推荐)
这个方案从应用启动阶段就直接过滤掉不需要注册的控制器,端点不会出现在路由表中,Swagger也不会展示,完全符合“不在C1中发布”的需求。
- 首先在
appsettings.json中配置当前集群服务的租户列表:
{ "TenantSettings": { "ServedTenants": ["T3", "T4"] // C2集群的配置,C1集群则配置为["T1", "T2"] } }
- 在Program.cs中读取配置,判断是否注册目标控制器:
var builder = WebApplication.CreateBuilder(args); // 读取当前集群服务的租户列表 var servedTenants = builder.Configuration.GetSection("TenantSettings:ServedTenants").Get<string[]>(); // 注册控制器,按需包含目标控制器所在程序集 builder.Services.AddControllers() .ConfigureApplicationPartManager(apm => { // 获取目标控制器所在的程序集部件 var targetAssemblyPart = apm.ApplicationParts .FirstOrDefault(p => p.Name == typeof(TargetController).Assembly.GetName().Name); if (targetAssemblyPart != null) { // 仅当当前集群服务T3/T4时保留该部件,否则移除 var shouldKeep = servedTenants?.Any(t => t is "T3" or "T4") == true; if (!shouldKeep) { apm.ApplicationParts.Remove(targetAssemblyPart); } } }); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
方案二:自定义特性+全局筛选器
如果需要更灵活的租户匹配规则,或者不想修改程序集注册逻辑,可以用全局筛选器实现控制器级别的访问控制:
- 创建一个标记控制器需要特定租户的特性:
[AttributeUsage(AttributeTargets.Class)] public class RequireTenantsAttribute : Attribute { public string[] AllowedTenants { get; } public RequireTenantsAttribute(params string[] allowedTenants) { AllowedTenants = allowedTenants; } }
- 在目标控制器上标记该特性:
[RequireTenants("T3", "T4")] [ApiController] [Route("api/[controller]")] public class TargetController : ControllerBase { // 控制器方法逻辑 }
- 实现全局筛选器,检查当前集群是否允许访问该控制器:
public class TenantControllerFilter : IActionFilter { private readonly string[] _servedTenants; public TenantControllerFilter(string[] servedTenants) { _servedTenants = servedTenants ?? Array.Empty<string>(); } public void OnActionExecuting(ActionExecutingContext context) { var requireAttr = context.Controller.GetType().GetCustomAttribute<RequireTenantsAttribute>(); if (requireAttr != null) { var hasPermission = requireAttr.AllowedTenants.Intersect(_servedTenants).Any(); if (!hasPermission) { context.Result = new NotFoundResult(); } } } public void OnActionExecuted(ActionExecutedContext context) { } }
- 在Program.cs中注册筛选器:
var builder = WebApplication.CreateBuilder(args); var servedTenants = builder.Configuration.GetSection("TenantSettings:ServedTenants").Get<string[]>(); builder.Services.AddControllers(options => { options.Filters.Add(new TenantControllerFilter(servedTenants)); }); var app = builder.Build(); // 中间件配置... app.MapControllers(); app.Run();
方案三:端点路由的条件映射
如果只需要控制特定端点的发布,而非整个控制器,可以在端点路由阶段条件注册:
var app = builder.Build(); var servedTenants = app.Configuration.GetSection("TenantSettings:ServedTenants").Get<string[]>(); app.UseHttpsRedirection(); app.UseAuthorization(); // 映射所有常规控制器端点 app.MapControllers(); // 仅当当前集群服务T3/T4时,映射目标控制器的端点 if (servedTenants?.Any(t => t is "T3" or "T4") == true) { app.MapControllerRoute( name: "target-controller", pattern: "api/target/{action}", defaults: new { controller = "Target" }); } app.Run();
内容的提问来源于stack exchange,提问作者Jorgoth
相关产品推荐
相关产品推荐

