zoukankan      html  css  js  c++  java
  • 在 ASP.NET Web API 中,使用 命名空间(namespace) 来作为路由的参数

    这个问题来源于我想在 Web API 中使用相同的控制器名称(Controller)在不同的命名空间下,但是 Web API 的默认 路由(Route) 机制是会忽略命名空间的不同的,如果这样做,会看到以下提示:

    找到多个与名为“XXX”的控制器匹配的类型。如果为此请求(“{namespace}/{controller}/{action}”)提供服务的路由找到多个控制器,并且这些控制器是使用相同的名称但不同的命名空间定义的(这不受支持),则会发生这种情况。

    在 ASP.NET MVC 中,可以通过建立 区域(Area) 来解决这种问题,但 Web API 并没有区域这种东西。

     

    不过官方早已给出了解决方案,只是并没有作为 Web API 的一部分直接集成。不知道是出于什么考虑。

    原文链接:http://blogs.msdn.com/b/webdev/archive/2013/03/08/using-namespaces-to-version-web-apis.aspx

     

    主要思路就是自己重新实现一个可以识别命名空间的路由选择器,然后替换掉系统默认的路由选择器即可。这个命名空间选择器官方也已经帮我们实现,并且提供了完整的项目演示示例。

    代码链接:http://aspnet.codeplex.com/SourceControl/changeset/view/dd207952fa86#Samples/WebApi/NamespaceControllerSelector/NamespaceHttpControllerSelector.cs

     

    将此类的实现加入到项目中,并在初始化 Web API 路由时进行替换,在设置路由模板的时候,加入相应的 {namespace} 参数即可:

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
    
            config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config));
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "{namespace}/{controller}/{action}"
            );
        }
    }

     

    最后,附上官方 NamespaceHttpControllerSelector 类的实现代码(出处请见上述链接):

    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;
        }
    }
  • 相关阅读:
    C# WinForm程序退出的方法
    SpringCloud 微服务框架
    idea 常用操作
    Maven 学习笔记
    SpringBoot 快速开发框架
    html 零散问题
    Java方法注释模板
    Seating Arrangement
    hibernate 离线查询(DetachedCriteria)
    hibernate qbc查询
  • 原文地址:https://www.cnblogs.com/xwgli/p/4457628.html
Copyright © 2011-2022 走看看