MVC5为WebAPI添加命名空间的支持
2017-06-19 14:51
375 查看
前言
默认情况下,微软提供的MVC框架模板中,WebAPI路由是不支持Namespace参数的。这导致一些比较大型的项目,无法把WebApi分离到单独的类库中。本文将提供解决该问题的方案。
微软官方曾经给出过一个关于WebAPI支持Namespace的扩展,其主要内容就是自定义实现了IHttpControllerSelector接口,通过路由配置时替换掉MVC中自带的DefaultHttpControllerSelector达到WebAPI支持Namespace的目的。但是经过我的测试,效果并不好。这就就不贴微软官方的源码了。
解决方案
下面我介绍一下我的解决方案。首先看一下我自定义的目录结构,如下图:
首先定义一个类,名字可以随意,我这里命名为ZhuSirNamespaceHttpControllerSelector,他继承自MVC框架默认的DefaultHttpControllerSelector类,该继承类主要目的是在请求URI到达WebAPI路由时检索我们指定的命名空间的WebAPI控制器。下面是ZhuSirNamespaceHttpControllerSelector类的源代码,可直接复制到你自己的项目中就可以用:
/// <summary> /// 扩展自DefaultHttpControllerSelector类的控制器选择器,目前在用 /// </summary> public class ZhuSirNamespaceHttpControllerSelector : DefaultHttpControllerSelector { private const string NamespaceRouteVariableName = "namespaces"; private readonly HttpConfiguration _configuration; private readonly Lazy<ConcurrentDictionary<string, Type>> _apiControllerCache; public ZhuSirNamespaceHttpControllerSelector(HttpConfiguration configuration) : base(configuration) { _configuration = configuration; _apiControllerCache = new Lazy<ConcurrentDictionary<string, Type>>( new Func<ConcurrentDictionary<string, Type>>(InitializeApiControllerCache)); } private ConcurrentDictionary<string, Type> InitializeApiControllerCache() { IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver(); var types = this._configuration.Services.GetHttpControllerTypeResolver() .GetControllerTypes(assembliesResolver).ToDictionary(t => t.FullName, t => t); return new ConcurrentDictionary<string, Type>(types); } public IEnumerable<string> GetControllerFullName(HttpRequestMessage request, string controllerName) { object namespaceName; var data = request.GetRouteData(); IEnumerable<string> keys = _apiControllerCache.Value.ToDictionary<KeyValuePair<string, Type>, string, Type>(t => t.Key, t => t.Value, StringComparer.CurrentCultureIgnoreCase).Keys.ToList(); if (!data.Values.TryGetValue(NamespaceRouteVariableName, out namespaceName)) { return from k in keys where k.EndsWith(string.Format(".{0}{1}", controllerName, DefaultHttpControllerSelector.ControllerSuffix), StringComparison.CurrentCultureIgnoreCase) select k; } string[] namespaces = (string[])namespaceName; return from n in namespaces join k in keys on string.Format("{0}.{1}{2}", n, controllerName, DefaultHttpControllerSelector.ControllerSuffix).ToLower() equals k.ToLower() select k; } public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { Type type; if (request == null) { throw new ArgumentNullException("request"); } string controllerName = this.GetControllerName(request); if (string.IsNullOrEmpty(controllerName)) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("无法通过API路由匹配到您所请求的URI '{0}'", new object[] { request.RequestUri }))); } IEnumerable<string> fullNames = GetControllerFullName(request, controllerName); if (fullNames.Count() == 0) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("无法通过API路由匹配到您所请求的URI '{0}'", new object[] { request.RequestUri }))); } if (this._apiControllerCache.Value.TryGetValue(fullNames.First(), out type)) { return new HttpControllerDescriptor(_configuration, controllerName, type); } throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("无法通过API路由匹配到您所请求的URI '{0}'", new object[] { request.RequestUri }))); } }
第二步,需要我们配置WebAPI路由设置,添加{ namespaces }片段变量,同时也可以直接为其设置默认值,然后替换掉原MVC框架中的DefaultHttpControllerSelector选额器为我们之前扩展的ZhuSirNamespaceHttpControllerSelector选额器。这里需要注意片段变量的变量名namespaces一定要与我们ZhuSirNamespaceHttpControllerSelector中定义的NamespaceRouteVariableName字符串常量的值一致。下面贴出WebApiConfig的源码:[/code]
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//配置检查Api控制后缀为ApiController
//var suffix = typeof(MicrosoftNamespaceHttpControllerSelector)
// .GetField("ControllerSuffix", BindingFlags.Static | BindingFlags.Public);
//if (suffix != null)
//{
// suffix.SetValue(null, "ApiController");
//}
// Web API 配置和服务
// 将 Web API 配置为仅使用不记名令牌身份验证。
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// 对 JSON 数据使用混合大小写。
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
// Web API 路由
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{namespaces}",
defaults: new { id = RouteParameter.Optional ,namespaces = new[] { "ZhuSir.HMS.WebApi.ApiControllers" } }
);
config.Services.Replace(typeof(IHttpControllerSelector), new ZhuSirNamespaceHttpControllerSelector(config));
}
}
如上源码。我们替换了原MVC框架的DefaultHttpControllerSelector为ZhuSirNamespaceHttpControllerSelector,并且指定了默认的namespaces为ZhuSir.HMS.WebApi.ApiControllers。大体意思就是当URL为 http://XXX/api/testApi时,WebApi路由将在ZhuSir.HMS.WebApi.ApiControllers命名空间下寻找名称为testApi的WebAPI控制器的Get方法。
当然,WebAPI控制器除了集成字ApiController意外还要注意命名规范,都需要以Controller结尾,为了不让API控制器与MVC控制器重名,我都以ApiController结尾。下面贴出testAPI的源码:
namespace ZhuSir.HMS.WebApi.ApiControllers { public class TestApiController : ApiController { [HttpGet] public string Gettest() { return "测试数据"; } } }
测试
程序Debug,录入URL: http://localhost:4541/api/TestApi,得到如下结果:可以看出,WebAPI路由成功访问到了其他类库中的WebAPI控制器。
相关文章推荐
- MVC5为WebAPI添加命名空间的支持
- MVC5为WebAPI添加命名空间的支持1
- asp.net MVC5为WebAPI添加命名空间的支持
- PHPStorm 添加支持 PSR-4 命名空间前缀设置
- VS2005的Excel操作(一) - 添加Excel命名空间的支持
- WebApi支持命名空间重名问题
- XML文件用XMLSchema进行验证和添加命名空间后节点的筛选
- 在类库中添加引用System.Web.SessionState 命名空间中遇到的问题(已解决)
- CDATA C#操作XML(无命名空间),添加/删除/编辑节点
- .NET添加引用后,但在代码中使用using时却找不到命名空间?
- 修改Visual Studio中“添加新项”时默认添加的命名空间
- C# 添加了 reference 后 仍然报 找不到命名空间的问题
- C# 自动添加命名空间快捷键
- MVC在web.config中添加命名空间
- 【原创】包含CDATA C#操作XML(无命名空间),添加/删除/编辑节点
- 添加命名空间后XPath的使用问题
- Visual Studio 2005/2008 C#或ASP.NET项目中添加ActiveDirector相关的命名空间
- 命名空间怎么添加说明
- dom4j 为生成 XML 的文件添加 xmlns(命名空间) 属性
- .NET添加引用后,但在代码中使用using时却找不到命名空间?