WebAPI增加Area以支持无限层级同名Controller

原文:WebAPI增加Area以支持无限层级同名Controller

微软的WebAPI默认实现逻辑

默认实现中不支持同名Controller,否则在访问时会报HttpError,在网上找到了各种路由自实现,如

给ASP.net Web API的Controller分类

搭建MVC及WebAPI项目框架时碰到的问题集合

在上述地址的帮助下,根据需求,重新编写了AreaHttpControllerSelector,路由原理与上述地址大同小异,均是通过路由匹配拼接FullName,然后匹配最接近的ApiController,而所谓的最接近,就是指如果根据拼接的Name获取到了多个匹配项,则获取命名空间节点数最少的那个ApiController,以保证在多次注册路由规则时,能够按照从繁到简的方式匹配出相应的Controller(需要注意的是AreaHttpControllerSelector是以controller作为结束分割点的),举例如下

假定注册了以下路由匹配规则(controller、action均为WebAPI的路由占用字符)

           config.Routes.MapHttpRoute(
                name: "DefaultAreaApi",
                routeTemplate: "api/{area}/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

在Controller目录下存在多层同名且不同层级的Controller,如:

Controller/Area/SameController,对应的命名空间为Controller.Area.SameController
Controller/SameController,对应的命名空间为Controller.SameController

通过api/Area/Same/Get将匹配到Controller/Area/SameController
通过api/Same/Get将匹配到Controller/SameController

相比于参考网址,重新编写的AreaHttpControllerSelector可以支持无限层级的区域,只要命名空间支持,比如

"api/{area1}/{area1}/{area2}/{area3}/{controller}/{action}/{id}"

以下是具体的AreaHttpControllerSelector代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Dispatcher;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Net;

namespace WebAPI
{
    /// <summary>
    /// Represents a area System.Web.Http.Dispatcher.IHttpControllerSelector instance
    /// </summary>
    public class AreaHttpControllerSelector : DefaultHttpControllerSelector
    {
        private readonly HttpConfiguration _configuration;
        /// <summary>
        /// Lazy 当前程序集中包含的所有IHttpController反射集合,TKey为小写的Controller
        /// </summary>
        private readonly Lazy<ILookup<string, Type>> _apiControllerTypes;
        private ILookup<string, Type> ApiControllerTypes
        {
            get
            {
                return this._apiControllerTypes.Value;
            }
        }
        /// <summary>
        /// Initializes a new instance of the AreaHttpControllerSelector class
        /// </summary>
        /// <param name="configuration"></param>
        public AreaHttpControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            this._configuration = configuration;
            this._apiControllerTypes = new Lazy<ILookup<string, Type>>(this.GetApiControllerTypes);
        }
        /// <summary>
        /// 获取当前程序集中 IHttpController反射集合
        /// </summary>
        /// <returns></returns>
        private ILookup<string, Type> GetApiControllerTypes()
        {
            IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver();
            return this._configuration.Services.GetHttpControllerTypeResolver()
                .GetControllerTypes(assembliesResolver)
                .ToLookup(t => t.Name.ToLower().Substring(0, t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length), t => t);
        }
        /// <summary>
        /// Selects a System.Web.Http.Controllers.HttpControllerDescriptor for the given System.Net.Http.HttpRequestMessage.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            HttpControllerDescriptor des = null;
            string controllerName = this.GetControllerName(request);
            if (!string.IsNullOrWhiteSpace(controllerName))
            {
                var groups = this.ApiControllerTypes[controllerName.ToLower()];
                if (groups != null && groups.Any())
                {
                    string endString;
                    var routeDic = request.GetRouteData().Values;//存在controllerName的话必定能取到IHttpRouteData
                    if (routeDic.Count > 1)
                    {
                        StringBuilder tmp = new StringBuilder();
                        foreach (var key in routeDic.Keys)
                        {
                            tmp.Append('.');
                            tmp.Append(routeDic[key]);
                            if (key.Equals(DefaultHttpControllerSelector.ControllerSuffix, StringComparison.CurrentCultureIgnoreCase))
                            {//如果是control,则代表命名空间结束
                                break;
                            }
                        }
                        tmp.Append(DefaultHttpControllerSelector.ControllerSuffix);
                        endString = tmp.ToString();
                    }
                    else
                    {
                        endString = string.Format(".{0}{1}", controllerName, DefaultHttpControllerSelector.ControllerSuffix);
                    }
                    //取NameSpace节点数最少的Type
                    var type = groups.Where(t => t.FullName.EndsWith(endString, StringComparison.CurrentCultureIgnoreCase))
                        .OrderBy(t => t.FullName.Count(s => s == '.')).FirstOrDefault();//默认返回命名空间节点数最少的第一项
                    if (type != null)
                    {
                        des = new HttpControllerDescriptor(this._configuration, controllerName, type);
                    }
                }
            }
            if (des == null)
            {
                throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound,
                    string.Format("No route providing a controller name was found to match request URI '{0}'", request.RequestUri)));
            }
            return des;
        }
    }
}

而用法就是在Global文件的Application_Start方法中替换注册

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),
                                                   new AreaHttpControllerSelector(
                                                       GlobalConfiguration.Configuration));
时间: 2024-11-16 11:37:35

WebAPI增加Area以支持无限层级同名Controller的相关文章

一棵php的类树(支持无限分类)

最近问无限分类的类树问题比较多,所以一高兴自己写了一个,我刚写完的,大家用用看,看看怎么实现起来更快,更简单,把你的树也贴出来(要只查询一次数据库的)<br> 这是一棵分类列表的类树, 支持无限分类<br> 一个分类下面可以同时有"包含子类的分类"和"最终分类";<br> 唯一的优点是*****只需要进行一次的数据库*****查询.<br> 样子不是很好看,不过可以自定义修改,可以自己定义css加里面<br>

一棵php的类树(支持无限分类)_php基础

最近问无限分类的类树问题比较多,所以一高兴自己写了一个,我刚写完的,大家用用看,看看怎么实现起来更快,更简单,把你的树也贴出来(要只查询一次数据库的)<br>  这是一棵分类列表的类树,  支持无限分类<br>  一个分类下面可以同时有"包含子类的分类"和"最终分类";<br>  唯一的优点是*****只需要进行一次的数据库*****查询.<br>  样子不是很好看,不过可以自定义修改,可以自己定义css加里面<

QBlog博客 V2.5 版本发布 增加健康频道[支持多语言、多用户、多数据库、目录级URL]

QBlog发布历史回顾: 1:2010年11月08日---支持多语言.多用户.多数据库.目录级URL之路过秋天版博客发布[绝对有杀伤力的博客] 2:2010年11月10日---基础却容易被忽略的那点事--web入侵方式及注意事项总结 3:2010年11月15日---支持多语言.多用户.多数据库.目录级URL之路过秋天版博客 V1.0正式版 发布[含详细部署安装说明] 4:2010年11月17日---路过秋天版博客-皮肤制作指南 [附犀利哥入侵攻防站话题] 5:2010年11月29日---路过秋天

路过秋天版博客 V2.0 正式版发布 增加后台管理系统[支持多语言、多用户、多数据库、目录级URL]

路过秋天版博客发布历史回顾: 1:2010年11月08日---支持多语言.多用户.多数据库.目录级URL之路过秋天版博客发布[绝对有杀伤力的博客] 2:2010年11月10日---基础却容易被忽略的那点事--web入侵方式及注意事项总结 3:2010年11月15日---支持多语言.多用户.多数据库.目录级URL之路过秋天版博客 V1.0正式版 发布[含详细部署安装说明] 4:2010年11月17日---路过秋天版博客-皮肤制作指南 [附犀利哥入侵攻防站话题] 5:2010年11月29日---路过

Opscode在Chef增加活动目录支持

由于云计算的快速发展,http://www.aliyun.com/zixun/aggregation/13879.html">系统管理员需要更好的和更多的自动化工具来加速虚拟服务器以满足需求.这就是为什么Opscode推动Private Chef的可扩展性,使系统管理员无需增加硬件就可以管理平日三倍的工作.Opscode还增加了支持微软的Active Directory和Solaris. 在配置管理方面,Opscode和Puppet Labs是主要的竞争对手.这些公司已成为云供应商的&qu

苹果iOS地图增加3D Flyover支持 包括迪斯尼乐园

&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp;   在最近一次的iOS Maps应用更新中,苹果增加了加州大片地区的Flyover支持,其中便包括了在阿纳海姆(Anaheim)的 迪斯尼乐园等著名旅游景点.经外媒 AppleInsider上 网友Mike M.的首次爆料,本次更新显示了苹果对于其地图资产的积极态度--尽管该服务自去年9月发布以来便备受指责. Flyover视图:作为迪斯尼乐园.右为圣塔莫妮卡码头.

Suprema RFID指纹扫描仪增加安卓设备支持

生物识别和ID解决方案供应商Suprema宣布其双重认证RFID指纹扫描仪BioMini Combo增加对安卓设备的支持. (Suprema的BioMini Combo) BioMini Combo可提供接触及非接触智能卡及NFC指纹识别结合的双重认证解决方案.该扫描仪内置一个大压盘光学处理器以及Suprema的活指指纹检测技术.同时,该扫描仪通过来FBI PIV及Mobile ID (FAP20)的指纹识别认证,并且支持各种RF标准,包括接触式或非接触式(RFID)智能卡. 通过支持安卓系统,

AutoLoadCache 6.0 增加二级缓存支持

AutoLoadCache 6.0 已经发布.AutoLoadCache 是基于AOP+Annotation等技术实现的高效的缓存管理解决方案,实现缓存与业务逻辑的解耦,并增加异步刷新及"拿来主义机制",以适应高并发环境下的使用.新版本更新内容: 增加二级缓存的支持(即本地缓存和分布式缓存).实现类在:com.jarvis.cache.ComboCacheManager.增加了@LocalCache来配置本地缓存.此版二级缓存,没有解决一致性问题. 代码结构做了调整.如果是从老版本升级

谷歌再次更新航班搜索业务 增加更多语言支持

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 5月31日消息,据国外媒体报道,谷歌在上周推出新的航班搜索服务后,受到市场和消费者的热烈反响.该服务让查找航班信息就像查公交或者火车班次一样简单方便. 仅需在搜索框中输入起点或终点的机场,相关的航线就会呈现出来,所有航线都有标明航班号.日期和时间. 目前,谷歌在官方博客中表示,公司再次对此项服务进行更新,将增加更多的航班信息,让消费者更简便查