在 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
主要思路就是自己重新实现一个可以识别命名空间的路由选择器,然后替换掉系统默认的路由选择器即可。这个命名空间选择器官方也已经帮我们实现,并且提供了完整的项目演示示例。
将此类的实现加入到项目中,并在初始化 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;
}
}
在 ASP.NET Web API 中,使用 命名空间(namespace) 来作为路由的参数的更多相关文章
- ASP.NET Web API中的Controller
虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要 ...
- 【ASP.NET Web API教程】6.2 ASP.NET Web API中的JSON和XML序列化
谨以此文感谢关注此系列文章的园友!前段时间本以为此系列文章已没多少人关注,而不打算继续下去了.因为文章贴出来之后,看的人似乎不多,也很少有人对这些文章发表评论,而且几乎无人给予“推荐”.但前几天有人询 ...
- Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化
前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET W ...
- 【ASP.NET Web API教程】4.3 ASP.NET Web API中的异常处理
原文:[ASP.NET Web API教程]4.3 ASP.NET Web API中的异常处理 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内 ...
- ASP.NET Web API中的JSON和XML序列化
ASP.NET Web API中的JSON和XML序列化 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok ...
- 目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的选择
目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的选择 ASP.NET Web API能够根据请求激活目标HttpController ...
- 在ASP.NET Web API中使用OData
http://www.alixixi.com/program/a/2015063094986.shtml 一.什么是ODataOData是一个开放的数据协议(Open Data Protocol)在A ...
- ASP.NET Web API 中的异常处理(转载)
转载地址:ASP.NET Web API 中的异常处理
- ASP.NET WEB API 中的路由调试与执行过程跟踪
路由调试 RouteDebugger 是调试 ASP.NET MVC 路由的一个好的工具,在ASP.NET WEB API中相应的有 WebApiRouteDebugger ,Nuget安装 Inst ...
- 能省则省:在ASP.NET Web API中通过HTTP Headers返回数据
对于一些返回数据非常简单的 Web API,比如我们今天遇到的“返回指定用户的未读站内短消息数”,返回数据就是一个数字,如果通过 http response body 返回数据,显得有些奢侈.何不直接 ...
随机推荐
- 【转载】Remote System Explorer Operation总是运行后台服务,卡死eclipse解决办法
原来是eclipse后台进程在远程操作,就是右下角显示的“Remote System Explorer Operation”.折腾了半天,在Stack Overflow找到答案(源地址).把解决方案翻 ...
- 本地文件夹变远程仓库并且提交Github
//初始化本地仓库 $ git init Initialized empty Git repository in C:/Users/root/Desktop/vue-music/.git/ root@ ...
- which命令(转)
原文:http://www.cnblogs.com/peida/archive/2012/11/08/2759805.html 我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一 ...
- Memcache and Mongodb
转自:http://www.cnblogs.com/lovecindywang/archive/2010/05/19/1739025.html 先说说自己对Memcache和Mongodb的一些看法. ...
- ORA-27090 故障一例
近期的alert日志中碰到了ORA-27090的错误信息.其错误提示为Unable to reserve kernel resources for asynchronous disk I/O.依据这个 ...
- LeetCode(35):Palindrome Number
Determine whether an integer is a palindrome. Do this without extra space. Some hints: Could negativ ...
- GitLab Notification Emails
GitLab has a notification system in place to notify a user of events that are important for the work ...
- [ubuntu]为ubuntu设立“任务管理器”的组合键
在windows下面,我们可以方便的使用ctrl+alt+delete调出任务管理器,那么在ubuntu下面如何实现呢?这里我们介绍两种方法:1.在终端下运行: 代码:gconf-editor 找到: ...
- cxf之GET,POST,PUT,DELETE的区别
GET,POST,PUT,DELETE的区别 注意: 文中有错误地方:关于增改删查正确的描述应为: get对应的是查询post对应的是保存/增加delete对应的是删除put对应的是更新
- 用 python 爬虫抓站的一些技巧总结
学用python也有3个多月了,用得最多的还是各类爬虫脚本:写过抓代理本机验证的脚本,写过在discuz论坛中自动登录自动发贴的脚本,写过自动收邮件的脚本,写过简单的验证码识别的脚本,本来想写goog ...