如何允许插件webAPI覆盖现有的webAPI

时间:2017-07-17 09:57:04

标签: c# asp.net-web-api autofac

我有一个Web API控制器,比如EmployeeController,我们使用Autofac进行注册。现在我们创建另一个具有相同名称和路由但具有不同功能的控制器。当我们尝试使用 Autofac 注册这个新的EmployeeController(即插件)时,我们会得到一个例外

  

找到了与名为EmployeeController的控制器匹配的多个类型。

我的目标是成功注入第二个控制器并用它覆盖第一个控制器的功能。

项目A - >核心项目

namespace Main.API
{
    public class EmployeeController : ApiController
    {
        // Some Logic
    }    
}

项目B - >插件项目

以后的消费者希望覆盖具有相同控制器名称的员工控制器

namespace Plugin.API
{
    public class EmployeeController : ApiController
    {
        // Some Logic
    }    
}

Autofac

// assemblies contains Main.API.dll & Plugin.API.dll
builder.RegisterApiControllers(assemblies.ToArray()).InstancePerRequest();

2 个答案:

答案 0 :(得分:0)

为了实现你想要的东西,我会使用AOP concept,这将使它更容易实现,更强大。

Castle DynamicProxy项目为.net提供了AOP概念, Autofac 可以使用Autofac.Extras.DynamicProxy2 nuget包。

您的主项目中只有1 EmployeeController

namespace Main.API
{
    public class EmployeeController : ApiController
    {
        public virtual String Get(Int32 id)
        {
           // Some Logic
        }
    }    
}

以及插件项目中的各种IInterceptor

namespace Plugin 
{
    public class XEmployeeeControllerInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            if(!invocation.Method.Name == nameof(Core.APi.EmployeeController.Get))
            {
                return; 
            }
            invocation.Proceed(); 

            // alter return value 
            invocation.ReturnValue = invocation.ReturnValue + "-intercepted";
        }
    }
}

然后注册这样的事情:

builder.RegisterApiControllers(assemblies.ToArray())
       .InstancePerRequest()
       .EnableClassInterceptors();

builder.RegisterAssemblyTypes(assemblies.ToArray())
       .As<IInterceptor>(); 

有关详细信息,请参阅Type Interceptors

答案 1 :(得分:0)

使用以下代码段,您可以覆盖插件控制器的相同名称。

public class CustomHttpControllerSelector : DefaultHttpControllerSelector
    {
        private readonly HttpConfiguration _configuration;
        private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;


    /// <summary>
    /// custom http controllerselector
    /// </summary>
    /// <param name="config"></param>
    public CustomHttpControllerSelector(HttpConfiguration config) : base(config)
    {
        _configuration = config;
        _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
    }

    /// <summary>
    /// GetControllerMapping
    /// </summary>
    /// <returns></returns>
    public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
    {
        return _controllers.Value;
    }

    private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
    {
        var controllers = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);

        IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
        IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
        ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);

        foreach (Type t in controllerTypes)
        {
            var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);

            //Remove Core API Controller and add the Plugin API controller.
            if (controllers.Keys.Contains(controllerName) && t.Namespace.Contains("Plugin"))
            {
                controllers.Remove(controllerName);
            }
            if (!controllers.Keys.Contains(controllerName))
            {
                controllers[controllerName] = new HttpControllerDescriptor(_configuration, t.Nam`enter code here`e, t);
            }
        }
        return controllers;
    }
}