原文: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. js进阶 10-10 可见伪类选择器和内容伪类选择器的作用

    js进阶 10-10  可见伪类选择器和内容伪类选择器的作用 一.总结 一句话总结:分组来描述.内容伪类选择器就是  四个  包含.可见的伪类选择器就是可见和不可见.查找功能,也就是内容伪类选择器非常 ...

  2. spring boot + gradle + mybatis

    使用intelliJ创建 spring boot + gradle + mybatis站点   Spring boot作为快速入门是不错的选择,现在似乎没有看到大家写过spring boot + gr ...

  3. 【转】关于python中带下划线的变量和函数 的意义

    http://www.blogjava.net/lincode/archive/2011/02/02/343859.html 总结: 变量: 1.  前带_的变量:  标明是一个私有变量, 只用于标明 ...

  4. Java中的浮点数-科学计数法-加减乘除

    上次,提到"元转分"这个浮点数问题,boss倾向于手动把1.23元这种格式,转换成123分.    但实际上,浮点数很容易遇到精度问题.    比如,System.out.prin ...

  5. 【Dijkstra+邻接表求次短路】POJ Sightseeing 3463

    Language: Default Sightseeing Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7766   Ac ...

  6. Docker for Windows 安装

    原文:Docker for Windows 安装 前言: 环境:windows10专业版 64位 正文: 官方下载地址:https://hub.docker.com/editions/communit ...

  7. 高性能分布式应用开发中间件ICE介绍

    作为一个技术人员,你是否在为不断增长的数据量和日益复杂的业务逻辑而头疼不已,杂乱堆砌在一起的庞大业务让系统越来越脆弱,于是你想到了网格,想到了利用分布式来重组一个健壮的系统架构. 随后,RMI,EJB ...

  8. freemarker中间split字符串切割

    freemarker中间split字符串切割 1.简易说明 split切割:用来依据另外一个字符串的出现将原字符串切割成字符串序列 2.举例说明 <#--freemarker中的split字符串 ...

  9. 在深入分析:Fragment与Activity一些互动的方式(一,使用Handler)

    在这里,我不再具体介绍了编写更传统的方式,比如静态变量,静态方法.持久性,application全局变量.发送和接收广播等等.. 首先让我们介绍使用Handler实现Fragment与Activity ...

  10. 数据集成工具Teiid Designer的环境搭建

    由于实验室项目要求的关系,看了些数据汇聚工具 Teiid 的相关知识.这里总结下 Teiid 的可视化配置工具 Teiid Designer 的部署过程. 背景知识 数据集成是把不同来源.格式.特点性 ...