原文: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));

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

  1. Java编程:将具有父子关系的数据库表数据转换为树形结构,支持无限层级

    在平时的开发工作中,经常遇到这样一个场景,在数据库中存储了具有父子关系的数据,需要将这些数据以树形结构的形式在界面上进行展示.本文的目的是提供了一个通用的编程模型,解决将具有父子关系的数据转换成树形结 ...

  2. 简单叨叨bootstrap按钮无限层级下拉菜单的实现

    0.写在前面的话 最近看书都懈怠了,又正值新项目,虽说并不是忙得不可开交,好吧我老实交待,我就是偷懒了其实,博客也没更.言归正传,对于前端的不熟悉现在确实是个让我头疼的事情,以至于一些功能要在网络上漫 ...

  3. 自动给 Asp.Net Core WebApi 增加 ApiVersionNeutral

    自动给 Asp.Net Core WebApi 增加 ApiVersionNeutral Intro 新增加一个 Controller 的时候,经常忘记在 Controller 上增加 ApiVers ...

  4. django 无限层级的评论

    一.摘要 拓展 django 官方的评论库,为评论提供无限层级的支持. 二.demo演示 访问链接: https://github.com/zmrenwu/django-mptt-comments 下 ...

  5. MVC5为WebAPI添加命名空间的支持

    前言 默认情况下,微软提供的MVC框架模板中,WebAPI路由是不支持Namespace参数的.这导致一些比较大型的项目,无法把WebApi分离到单独的类库中. 本文将提供解决该问题的方案. 微软官方 ...

  6. 自己定义 ViewGroup 支持无限循环翻页之三(响应回调事件)

    大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处,再次感谢 ################################ ...

  7. js treeData 树形数据结构 无限层级(转载)

    js实现无限层级树形数据结构(创新算法) 转载:https://blog.csdn.net/Mr_JavaScript/article/details/82817177 由于做项目的需要,把一个线性数 ...

  8. asp.net MVC5为WebAPI添加命名空间的支持

    前言 默认情况下,微软提供的MVC框架模板中,WebAPI路由是不支持Namespace参数的.这导致一些比较大型的项目,无法把WebApi分离到单独的类库中. 本文将提供解决该问题的方案. 微软官方 ...

  9. SQL查询无限层级结构的所有下级,所有上级

    无限层级结构的table1表,Id(主键),ParentId(父级id)查询某个Id的所有下级或所有上级,使用WITH AS,UNION ALL 查询 1.查询Id为1所有的下级 WITH T AS( ...

随机推荐

  1. C/C++ 笔试、面试题目大汇总2

    http://www.cnblogs.com/fangyukuan/archive/2010/09/18/1830493.html 一.找错题 试题1: void test1() { charstri ...

  2. 安装 Visual Studio,连接中国区 Azure

    中国数据中心 目前,中国区 Azure 有两个数据中心,在位置字段中显示为“中国北部”和“中国东部”. 在 Azure 上创建应用程序的区别 在中国区 Azure 上开发应用程序与在境外 Azure ...

  3. 一题多解(五) —— topK(数组中第 k 大/小的数)

    根据对称性,第 k 大和第 k 小,在实现上,是一致的,我们就以第 k 小为例,进行说明: 法 1 直接排序(sort(A, A+N)),当使用一般时间复杂度的排序算法时,其时间复杂度为 O(N2) ...

  4. SpringBoot使用jsp作为视图模板&常规部署

    springboot其实并不推荐使用jsp作为视图模板,其默认采用Thymeleaf作为模板,出于对其没有研究,故考虑目前阶段仍然使用jsp作为视图模板.下面就展开实践案例过程: 1.首先创建一个js ...

  5. 详解Qt,并举例说明动态编译(shared)和静态编译(static)以及debug and release 编译版本区别(可产生静态版的Debug版本,需要把-release 改为 –debug-and-release)

    作为初入Qt学习的新人,花了整整一两天时间,对Qt编译版本等问题进行了一步步探索,首先感谢网站博客中文章,开始也不是很明白一些几个问题: 1.Qt版本问题 作为初学者,可能下载时这么多版本,如何选择呢 ...

  6. jQuery中的Deferred和promise

    promise:http://www.alloyteam.com/2014/05/javascript-promise-mode/ 1 jQuery 中的 Deferred 和 Promises : ...

  7. WPF UserControl 的绑定事件、属性、附加属性

    原文:WPF UserControl 的绑定事件.属性.附加属性 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/Vblegend_2013/arti ...

  8. iText 制作PDF

    前言 由于在MVC项目中需要使用PDF,所以自己抽空也来看看itext,以便于丰富自己的知识吧.在此也简单的记录一下,说不定以后可能还用的到. 在此您可以下载你想使用的版本http://sourcef ...

  9. 1 Task的简单实用

    Task是thread和threadpool两者结合的产物,吸收了二者的优点  进一步添加了一些新的 优秀的功能. using System; using System.Threading.Tasks ...

  10. 2 DDD理论学习2 领域

    一个领域本质上可以理解为就是一个问题域,只要是同一个领域,那问题域就相同. 所以,只要我们确定了系统所属的领域,那这个系统的核心业务,即要解决的关键问题.问题的范围边界就基本确定了. 领域首先要拆分成 ...