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

ASP.NET Core(.NET 5)如何按路由/插件配置不同Service Container

可行实现路径

完全不需要自定义TypeInfo,通过ASP.NET Core原生的扩展点组合即可实现需求,具体步骤如下:


1. 标记插件控制器+统一路由前缀注册

  • 先定义一个插件控制器标记属性,给每个插件的控制器打上对应标识,存储插件唯一ID,不需要修改类型本身的元数据
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginControllerAttribute : Attribute
{
    // 插件唯一标识,用于匹配对应容器
    public string PluginId { get; set; }
}
  • 实现IApplicationModelConvention扩展,扫描所有带[PluginController]标记的控制器,按插件配置统一添加路由前缀,同时把插件ID存入控制器元数据供后续激活使用
public class PluginRouteConvention : IApplicationModelConvention
{
    private readonly IPluginConfigService _pluginConfigService;

    public PluginRouteConvention(IPluginConfigService pluginConfigService)
    {
        _pluginConfigService = pluginConfigService;
    }

    public void Apply(ApplicationModel application)
    {
        foreach (var controller in application.Controllers)
        {
            var pluginAttr = controller.Attributes.OfType<PluginControllerAttribute>().FirstOrDefault();
            if (pluginAttr == null) continue;
            
            // 读取插件配置的路由前缀
            string routePrefix = _pluginConfigService.GetRoutePrefix(pluginAttr.PluginId);
            // 给该插件所有控制器统一拼接前缀
            controller.Selectors.ToList().ForEach(selector =>
            {
                selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(
                    new AttributeRouteModel(new RouteAttribute(routePrefix)),
                    selector.AttributeRouteModel);
            });
        }
    }
}
  • 注册扩展到MVC配置
services.AddControllers(opt =>
{
    opt.Conventions.Add(new PluginRouteConvention(你的插件配置服务实例));
});

2. 自定义控制器激活器实现容器隔离

直接实现IControllerActivator替换默认激活逻辑,激活时从控制器元数据中拿到插件ID,调取对应插件的Castle Windsor容器解析实例,核心应用的控制器仍走默认激活逻辑:

public class PluginControllerActivator : IControllerActivator
{
    private readonly IControllerActivator _defaultActivator;
    private readonly IPluginContainerManager _pluginContainerManager;

    public PluginControllerActivator(IControllerActivator defaultActivator, IPluginContainerManager pluginContainerManager)
    {
        _defaultActivator = defaultActivator;
        _pluginContainerManager = pluginContainerManager;
    }

    public object Create(ControllerContext context)
    {
        var controllerDesc = context.ActionDescriptor as ControllerActionDescriptor;
        // 非控制器请求/核心应用控制器走默认逻辑
        if (controllerDesc == null) return _defaultActivator.Create(context);
        var pluginAttr = controllerDesc.ControllerTypeInfo.GetCustomAttribute<PluginControllerAttribute>();
        if (pluginAttr == null) return _defaultActivator.Create(context);
        
        // 从插件容器管理器取对应插件的Windsor容器,解析控制器实例
        var pluginContainer = _pluginContainerManager.GetContainer(pluginAttr.PluginId);
        return pluginContainer.Resolve(controllerDesc.ControllerTypeInfo.AsType());
    }

    public void Release(ControllerContext context, object controller)
    {
        var controllerDesc = context.ActionDescriptor as ControllerActionDescriptor;
        if (controllerDesc == null)
        {
            _defaultActivator.Release(context, controller);
            return;
        }
        var pluginAttr = controllerDesc.ControllerTypeInfo.GetCustomAttribute<PluginControllerAttribute>();
        if (pluginAttr == null)
        {
            _defaultActivator.Release(context, controller);
            return;
        }
        // 插件控制器由对应Windsor容器释放
        var pluginContainer = _pluginContainerManager.GetContainer(pluginAttr.PluginId);
        pluginContainer.Release(controller);
    }
}

替换默认的控制器激活器:

services.Replace(ServiceDescriptor.Singleton<IControllerActivator>(sp =>
{
    var defaultActivator = new DefaultControllerActivator(new TypeActivatorCache());
    var containerManager = sp.GetRequiredService<IPluginContainerManager>();
    return new PluginControllerActivator(defaultActivator, containerManager);
}));

3. 插件容器的公共服务共享配置

你可以把核心应用的公共服务注册为每个插件Windsor容器的父容器,或者直接把公共服务实例批量注册到每个插件容器中,即可实现插件既可以调用公共服务,也可以自行注册私有服务的需求。


4. 运行时插件控制器发现(可选)

如果插件是运行时动态加载的,直接用Application Parts加载插件程序集即可,不需要自定义类型逻辑,默认的控制器发现机制可以正常识别带[PluginController]标记的控制器:

var mvcBuilder = services.AddControllers();
foreach (var pluginAssembly in 加载到的所有插件程序集集合)
{
    mvcBuilder.AddApplicationPart(pluginAssembly);
}

注意事项
  • 插件控制器需要设置为public访问级别,默认的控制器发现规则会匹配后缀为Controller的类,如果你的插件控制器命名不符合规则,可以自定义ControllerFeatureProvider修改匹配逻辑
  • 如果需要除控制器之外的其他中间件、过滤器也能使用对应插件的容器,可以在中间件层匹配到插件路由前缀时,提前创建对应插件的服务作用域存入HttpContext.Items,后续组件直接从该作用域解析服务即可

内容的提问来源于stack exchange,提问作者Jens

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 07:45:03