指定在运行时执行哪个api控制器

指定在运行时执行哪个api控制器

问题描述:

我有两个控制器在同一个名称(假设它是XController)在两个不同的程序集(它们在两个不同的项目中)。指定在运行时执行哪个api控制器

一个只是返回模拟数据,第二个是真正的交易。

如果我试图访问控制器http(s)://.../api/X当然有一个例外,因为找到了两个控制器,它们的名称与uri匹配。

我的目标是能够轻松地运行web api,其中一个或另一个控制器是到达的控制器。

我知道我可以在#if <symbol> ... #endif中包装这些类,但是每次我想从一个XController切换到另一个时,我必须对这两个项目进行更改。

我想我可以从IHttpControllerSelector中得到一些东西,但也许这会被牵强附会(也许不是,我问)。

我该如何做到这一点?

执行suggested by Microsoft将使用涉及“命名空间”的自定义路由,并使用IHttpControllerSelector的自定义配置(就像您在您的问题中所建议的那样)。此代码是直接从链接的源代码衍生的,我只是把它作为在这里直接引用任何未来passerbys:

// used in your WebApiConfig class 
    config.Routes.MapHttpRoute(
     name: "DefaultApi", 
     routeTemplate: "api/{namespace}/{controller}/{id}", 
     defaults: new { id = RouteParameter.Optional } 
    ); 

    config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config)); 

NamespaceHttControllerSelector实现:

public class NamespaceHttpControllerSelector : IHttpControllerSelector 
{ 
    private const string NamespaceKey = "namespace"; 
    private const string ControllerKey = "controller"; 

    private readonly HttpConfiguration _configuration; 
    private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers; 
    private readonly HashSet<string> _duplicates; 

    public NamespaceHttpControllerSelector(HttpConfiguration config) 
    { 
     _configuration = config; 
     _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase); 
     _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary); 
    } 

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

     // Create a lookup table where key is "namespace.controller". The value of "namespace" is the last 
     // segment of the full namespace. For example: 
     // MyApplication.Controllers.V1.ProductsController => "V1.Products" 
     IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver(); 
     IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); 

     ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); 

     foreach (Type t in controllerTypes) 
     { 
      var segments = t.Namespace.Split(Type.Delimiter); 

      // For the dictionary key, strip "Controller" from the end of the type name. 
      // This matches the behavior of DefaultHttpControllerSelector. 
      var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length); 

      var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - 1], controllerName); 

      // Check for duplicate keys. 
      if (dictionary.Keys.Contains(key)) 
      { 
       _duplicates.Add(key); 
      } 
      else 
      { 
       dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t); 
      } 
     } 

     // Remove any duplicates from the dictionary, because these create ambiguous matches. 
     // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products". 
     foreach (string s in _duplicates) 
     { 
      dictionary.Remove(s); 
     } 
     return dictionary; 
    } 

    // Get a value from the route data, if present. 
    private static T GetRouteVariable<T>(IHttpRouteData routeData, string name) 
    { 
     object result = null; 
     if (routeData.Values.TryGetValue(name, out result)) 
     { 
      return (T)result; 
     } 
     return default(T); 
    } 

    public HttpControllerDescriptor SelectController(HttpRequestMessage request) 
    { 
     IHttpRouteData routeData = request.GetRouteData(); 
     if (routeData == null) 
     { 
      throw new HttpResponseException(HttpStatusCode.NotFound); 
     } 

     // Get the namespace and controller variables from the route data. 
     string namespaceName = GetRouteVariable<string>(routeData, NamespaceKey); 
     if (namespaceName == null) 
     { 
      throw new HttpResponseException(HttpStatusCode.NotFound); 
     } 

     string controllerName = GetRouteVariable<string>(routeData, ControllerKey); 
     if (controllerName == null) 
     { 
      throw new HttpResponseException(HttpStatusCode.NotFound); 
     } 

     // Find a matching controller. 
     string key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, controllerName); 

     HttpControllerDescriptor controllerDescriptor; 
     if (_controllers.Value.TryGetValue(key, out controllerDescriptor)) 
     { 
      return controllerDescriptor; 
     } 
     else if (_duplicates.Contains(key)) 
     { 
      throw new HttpResponseException(
       request.CreateErrorResponse(HttpStatusCode.InternalServerError, 
       "Multiple controllers were found that match this request.")); 
     } 
     else 
     { 
      throw new HttpResponseException(HttpStatusCode.NotFound); 
     } 
    } 

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

我来用这个土壤溶解,但也许它有一些我忽视的不受欢迎的副作用。有人可以告诉我,如果有什么问题吗?

protected void Application_Start() 
    { 
     var specificControllerTypes = new[] 
     { 
      typeof(Mocks.XController) 
     }; 
     var config = GlobalConfiguration.Configuration; 
     config.Services.Replace(
      typeof(IHttpControllerTypeResolver), 
      new DefaultHttpControllerTypeResolver(type => 
       type.IsVisible && 
       !type.IsAbstract && 
       typeof(IHttpController).IsAssignableFrom(type) && 
       type.Name.EndsWith(DefaultHttpControllerSelector.ControllerSuffix) && 
       !specificControllerTypes.Any(t => t != type && t.Name == type.Name) 
      ) 
     );