MVC Api 的跨项目路由
现有Momoda.Api项目,由于团队所有人在此项目下开发,导致耦合度太高,现从此接口项目中拆分出多个子项目从而避免对Momda.Api的改动导致“爆炸”
MVCApi的跨项目路由和MVC有解决方式有点不同
第一步:
首先在Momoda下创建域创建好之后把生成的YZCAreaRegistration文件移动到Areas目录下YZC目录外,然后排除YZC文件夹,像这样:
![](https://images2015.cnblogs.com/blog/578477/201611/578477-20161103175126815-731103180.png)
第二步:
创建一个新的MVCApi叫 YZC.WebAi,在资源管理器中用 YZC.WebAi文件夹下的内容替换掉Areas下的YZC域文件夹中的内容,并且只留下Controllers,Views和Models三个文件夹,其他的config和Global等都删除
第三步:
右键LYL解决方案添加现有项目在YZC域下找到工程文件 ( \LYL\Momoda.Api\Areas)
这样就把Momoda.Api下的YZC域给拆分出来,开发过程中只关注YZC.WebApi即可,目录结构如下图:
![](https://images2015.cnblogs.com/blog/578477/201611/578477-20161103175200361-709487545.png)
第四步:
更改YZCAreaRegistration域路由文件。
Api路由文件的更改是否和MVC跨域路由一样添加context.MapRoute方法的最后一个命名空间参数就能解决呢?答案当然没有那么简单。。。。。哈哈哈哈
我们现验证以下是否按照MVC跨项目路由的解决方案也同样适用于Api。
更改父API项目的域路由文件:
1 public override void RegisterArea(AreaRegistrationContext context) 2 { 3 context.MapRoute( 4 "YZC_default", 5 "YZC/{controller}/{action}/{id}", 6 new { action = "Index", id = UrlParameter.Optional }, 7 new string[] { "YZC.Controllers" }//添加域项目的控制层命名空间 8 ); 9 }
在子api项目中添加controller:
1 { 2 public class ChildrenController : ApiController 3 { 4 #region 跨项目路由测试 5 [HttpGet] 6 public string Test() 7 { 8 return "小良哥"; 9 } 10 #endregion 11 } 12 }
浏览器中测试结果:
没有路由到,表明MVC域的处理和Api域的处理是不同的
![](https://images2015.cnblogs.com/blog/578477/201611/578477-20161103175404705-909439006.png)
解决方案:
在父ApiMomoda.Api下的App_Start中新增叫CustomHttpControllerSelector的类并且继承DefaultHttpControllerSelector
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 自定义区域类 3 /// </summary> 4 public class CustomHttpControllerSelector: DefaultHttpControllerSelector 5 { 6 private const string NamespaceRouteVariableName = namespaceName"; 7 //namespaceName之后要用到 8 //private const string AreaRouteVariableName = "area"; 9 private readonly HttpConfiguration _configuration; 10 private readonly Lazy<ConcurrentDictionary<string, Type>> _apiControllerCache; 11 12 public CustomHttpControllerSelector(HttpConfiguration configuration) 13 : base(configuration) 14 { 15 _configuration = configuration; 16 17 _apiControllerCache = new Lazy<ConcurrentDictionary<string, Type>>( 18 new Func<ConcurrentDictionary<string, Type>>(InitializeApiControllerCache)); 19 } 20 21 22 private ConcurrentDictionary<string, Type> InitializeApiControllerCache() 23 { 24 IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver(); 25 var types = this._configuration.Services.GetHttpControllerTypeResolver() 26 .GetControllerTypes(assembliesResolver).ToDictionary(t => t.FullName, t => t); 27 28 return new ConcurrentDictionary<string, Type>(types); 29 } 30 31 public IEnumerable<string> GetControllerFullName(HttpRequestMessage request, string controllerName) 32 { 33 object namespaceName; 34 var data = request.GetRouteData(); 35 IEnumerable<string> keys = _apiControllerCache.Value.ToDictionary<KeyValuePair<string, Type>, string, Type>(t => t.Key, 36 t => t.Value, StringComparer.OrdinalIgnoreCase).Keys.ToList(); 37 38 if (!data.Values.TryGetValue(NamespaceRouteVariableName, out namespaceName)) 39 { 40 return from k in keys 41 where k.EndsWith(string.Format(".{0}{1}", controllerName, 42 DefaultHttpControllerSelector.ControllerSuffix), StringComparison.OrdinalIgnoreCase) 43 select k; 44 } 45 46 string[] namespaces = (string[])namespaceName; 47 return from n in namespaces 48 join k in keys on string.Format("{0}.{1}{2}", n, controllerName, 49 DefaultHttpControllerSelector.ControllerSuffix).ToLower() equals k.ToLower() 50 select k; 51 } 52 53 54 public override HttpControllerDescriptor SelectController(HttpRequestMessage request) 55 { 56 Type type; 57 if (request == null) 58 { 59 throw new ArgumentNullException("request"); 60 } 61 62 string controllerName = this.GetControllerName(request); 63 if (string.IsNullOrEmpty(controllerName)) 64 { 65 throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, 66 string.Format("No route providing a controller name was found to match request URI '{0}'", 67 new object[] { request.RequestUri }))); 68 } 69 70 IEnumerable<string> fullNames = GetControllerFullName(request, controllerName); 71 if (fullNames.Count() == 0) 72 { 73 throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, 74 string.Format("No route providing a controller name was found to match request URI '{0}'", 75 new object[] { request.RequestUri }))); 76 } 77 78 if (!this._apiControllerCache.Value.TryGetValue(fullNames.First(), out type)) 79 { 80 throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, 81 string.Format("No route providing a controller name was found to match request URI '{0}'", 82 new object[] { request.RequestUri }))); 83 } 84 85 return new HttpControllerDescriptor(_configuration, controllerName, type); 86 } 87 }
更改父API项目的域路由文件为:
1 namespace Momoda.Api 2 { 3 public class YZCAreaRegistration : AreaRegistration 4 { 5 public override string AreaName//域名称 6 { 7 get 8 { 9 return "YZC"; 10 } 11 } 12 public override void RegisterArea(AreaRegistrationContext context) 13 { 14 context.Routes.MapHttpRoute( 15 name: this.AreaName + "_default", 16 routeTemplate: "api/" + this.AreaName + "/{controller}/{action}/{id}", 17 defaults: new 18 { 19 id = RouteParameter.Optional, 20 namespaceName = new string[] { string.Format("{0}.Controllers",this.AreaName) } 21 //namespaceName名称要CustomHttpControllerSelector中NamespaceRouteVariableName的值一致 22 } 23 ); 24 } 25 } 26 }
这样就完成了吗?没有!!!必须把域YZC.WebApi生成路径设置为Momoda.Api的bin文件下
测试结果:
成功!
![](https://images2015.cnblogs.com/blog/578477/201611/578477-20161103175624565-2109154477.png)
域名称可加可不加同样成功
![](https://images2015.cnblogs.com/blog/578477/201611/578477-20161103175650424-490469490.png)
17:57:11,嘿嘿,下班啦
![](https://blog.52itstyle.com/usr/uploads/6359488940289160158582279.gif)
作者:Chaunce
GitHub:https://github.com/liuyl1992
公众号请搜:架构师高级俱乐部 SmartLife_com
![](https://files-cdn.cnblogs.com/files/xiaoliangge/640.gif)
声明:原创博客请在转载时保留原文链接或者在文章开头加上本人博客地址,如发现错误,欢迎批评指正。凡是转载于本人的文章,不能设置打赏功能等盈利行为