2016-10-10 57 views
0

我有兩個控制器在同一個名稱(假設它是XController)在兩個不同的程序集(它們在兩個不同的項目中)。指定在運行時執行哪個api控制器

一個只是返回模擬數據,第二個是真正的交易。

如果我試圖訪問控制器http(s)://.../api/X當然有一個例外,因爲找到了兩個控制器,它們的名稱與uri匹配。

我的目標是能夠輕鬆地運行web api,其中一個或另一個控制器是到達的控制器。

我知道我可以在#if <symbol> ... #endif中包裝這些類,但是每次我想從一個XController切換到另一個時,我必須對這兩個項目進行更改。

我想我可以從IHttpControllerSelector中得到一些東西,但也許這會被牽強附會(也許不是,我問)。

我該如何做到這一點?

回答

1

執行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; 
    } 
} 
0

我來用這個土壤溶解,但也許它有一些我忽視的不受歡迎的副作用。有人可以告訴我,如果有什麼問題嗎?

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) 
      ) 
     ); 
相關問題