我理解的路由做用有如下幾個html
實話實說MVC的路由我不多去作擴展,在MVC4時代,還會去重寫掉url的大小寫,而在MVC5以後,MVC自帶了配置去小寫化url。不過有一個配置仍是必需要提一下那就是Area,在你的系統達到必定規模以後,Controllers經過Area來管理將會變得更容易。這裏給出個人Area擴展,很簡單可是很重要,注意子類必須以AreaRegistration結尾,一樣遵循約定有限於配置的原則,固然你也能夠重寫。web
public abstract class AreaRegistrationBase : AreaRegistration { public override string AreaName { get { var item = GetType().Name; return item.Replace("AreaRegistration", ""); } } public override void RegisterArea(AreaRegistrationContext context) { context.MapLowerCaseUrlRoute( AreaName + "_default", AreaName.ToLower() + "/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); GlobalConfiguration.Configuration.Routes.MapHttpRoute( AreaName + "Api", "api/" + AreaName + "/{controller}/{action}/{id}", new { area = AreaName, id = RouteParameter.Optional, namespaceName = new[] { this.GetType().Namespace } } ); } }
上面MVC的路由也註冊了Api的路由,固然還有更好的方法,因爲WebApi,基本不須要Url.Action和HtmlAction,這種路由出棧的策略,不須要去經過路由生成Url,因此
咱們只須要作到路由的解析便可,而且webapi並無提供自帶的area機制,因此我擴展了DefaultHttpControllerSelector,獲取到路由中area和controller參數
而後完成拼接,而後反射類型查找,在最開始時候預先緩存了全部controller,因此性能還不錯,因爲原代碼是反編譯得到,因此linq部分有點糟糕,等我回公司拿到源代碼再修改。json
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; namespace Coralcode.Webapi.Route { public class AreaHttpControllerSelector : DefaultHttpControllerSelector { public static string CoralControllerSuffix = "ApiController"; private readonly HttpConfiguration _configuration; private readonly Lazy<ILookup<string, Type>> _apiControllerTypes; private ILookup<string, Type> ApiControllerTypes { get { return this._apiControllerTypes.Value; } } public AreaHttpControllerSelector(HttpConfiguration configuration) : base(configuration) { this._configuration = configuration; this._apiControllerTypes = new Lazy<ILookup<string, Type>>(new Func<ILookup<string, Type>>(this.GetApiControllerTypes)); } private ILookup<string, Type> GetApiControllerTypes() { return Enumerable.ToLookup<Type, string, Type>((IEnumerable<Type>) ServicesExtensions.GetHttpControllerTypeResolver(this._configuration.Services).GetControllerTypes(ServicesExtensions.GetAssembliesResolver(this._configuration.Services)), (Func<Type, string>) (t => t.Name.ToLower().Substring(0, t.Name.Length - AreaHttpControllerSelector.CoralControllerSuffix.Length)), (Func<Type, Type>) (t => t)); } public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { string controllerName = this.GetControllerName(request); if (!string.IsNullOrWhiteSpace(controllerName)) { List<Type> list = Enumerable.ToList<Type>(this.ApiControllerTypes[controllerName.ToLower()]); if (Enumerable.Any<Type>((IEnumerable<Type>) list)) { IDictionary<string, object> values = HttpRequestMessageExtensions.GetRouteData(request).Values; string endString; if (values.Count > 1) { StringBuilder stringBuilder = new StringBuilder(); if (values.ContainsKey("area")) { stringBuilder.Append('.'); stringBuilder.Append(values["area"]); stringBuilder.Append('.'); stringBuilder.Append("controllers"); } if (values.ContainsKey("controller")) { stringBuilder.Append('.'); stringBuilder.Append(values["controller"]); stringBuilder.Append(AreaHttpControllerSelector.CoralControllerSuffix); } endString = stringBuilder.ToString(); } else endString = string.Format(".{0}{1}", (object) controllerName, (object) AreaHttpControllerSelector.CoralControllerSuffix); Type controllerType = Enumerable.FirstOrDefault<Type>((IEnumerable<Type>) Enumerable.OrderBy<Type, int>(Enumerable.Where<Type>((IEnumerable<Type>) list, (Func<Type, bool>) (t => t.FullName.EndsWith(endString, StringComparison.CurrentCultureIgnoreCase))), (Func<Type, int>) (t => Enumerable.Count<char>((IEnumerable<char>) t.FullName, (Func<char, bool>) (s => (int) s == 46))))); if (controllerType != (Type) null) return new HttpControllerDescriptor(this._configuration, controllerName, controllerType); } } return base.SelectController(request); } } }
Controller激活這部分是MVC和Ioc結合的核心,Ioc有三大部分api
MVC須要和Ioc激活首先咱們關注的就是哪裏註冊和哪裏去實例化,因爲MVC使用的是約定優先於配置的方式,全部的Controller都是以「Controller」結尾,因此這裏我直接加載
程序集,而後找到全部類型以Controller結尾的去註冊就行了,激活的話有如下三種方式,難易程度依次遞增,嵌套深度也依次遞增,這裏我採用重寫
默認DefaultControllerFactory的方式去激活,這樣既能夠用本身的也結合其餘機制去實現,其餘方式你們能夠自行擴展。緩存
using Coralcode.Framework.Aspect.Unity; using Coralcode.Mvc.Resources; using System; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace Coralcode.Mvc.ControllerFactory { public class UnityControllerFactory : DefaultControllerFactory { public override IController CreateController(RequestContext requestContext, string controllerName) { return base.CreateController(requestContext, controllerName); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == (Type) null) throw new HttpException(404, Messages.MvcBase_NotFoundPage); if (!UnityService.HasRegistered(controllerType)) return base.GetControllerInstance(requestContext, controllerType); return (IController) UnityService.Resolve(controllerType); } } } //webapi 激活以下 using Coralcode.Framework.Aspect.Unity; using System; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; namespace Coralcode.Webapi.ControllerFactory { public class UnityControllerActivator : IHttpControllerActivator { public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { IHttpController httpController = (IHttpController) UnityService.Resolve(controllerType); HttpRequestMessageExtensions.RegisterForDispose(request, httpController as IDisposable); return httpController; } } }
這裏基本上都是Mvc的源代碼,只是增長了一個日誌功能而已,扒MVC源代碼你們必定去嘗試併發
using Coralcode.Framework.Log; using System; using System.Web; using System.Web.Mvc; namespace Coralcode.Mvc.Filters { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class LogExceptionAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { if (filterContext == null) throw new ArgumentNullException("filterContext"); Exception exception = filterContext.Exception; LoggerFactory.Instance.Error(exception.ToString()); if (filterContext.IsChildAction || filterContext.ExceptionHandled || (!filterContext.HttpContext.IsCustomErrorEnabled || new HttpException((string) null, exception).GetHttpCode() != 500) || !this.ExceptionType.IsInstanceOfType((object) exception)) return; this.HandlerViewResultException(filterContext); } private void HandlerViewResultException(ExceptionContext filterContext) { string controllerName = (string) filterContext.RouteData.Values["controller"]; string actionName = (string) filterContext.RouteData.Values["action"]; HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); ExceptionContext exceptionContext = filterContext; ViewResult viewResult1 = new ViewResult(); viewResult1.ViewName = this.View; viewResult1.MasterName = this.Master; viewResult1.ViewData = (ViewDataDictionary) new ViewDataDictionary<HandleErrorInfo>(model); viewResult1.TempData = filterContext.Controller.TempData; ViewResult viewResult2 = viewResult1; exceptionContext.Result = (ActionResult) viewResult2; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.Clear(); filterContext.HttpContext.Response.StatusCode = 500; filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; } } }
這裏統一處理了Ajax請求返回數據以ResultMessage返回,若是不是JsonResult選擇忽略,具體設計初衷能夠看Controller設計那一節.異步
using Coralcode.Framework.Models; using Coralcode.Mvc.ActionResults; using System.Web.Mvc; namespace Coralcode.Mvc.Filters { public class ResultMessageAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext filterContext) { JsonResult jsonResult = (JsonResult) (filterContext.Result as CustomJsonResult) ?? filterContext.Result as JsonResult; if (jsonResult == null) return; jsonResult.Data = this.GetResultMessage(jsonResult.Data); filterContext.Result = (ActionResult) jsonResult; } private object GetResultMessage(object data) { if (data is BaseMessage) return data; return (object) new ResultMessage(ResultState.Success, "Success", data); } } }
提升併發利器,最佳使用方式,這裏詳細的介紹能夠結合Artec的Controller同步和異步這一節,其中我我的推薦的就是採用返回Task<ActionResult>。ide
這裏會將Url中的參數做爲Model綁定的數據源。在作三級菜單的時候能夠體現出用途,例如我首先在數據字典中添加以下數據模塊化
var newsType = new Glossary() { Key = "NewsType", Value = "NewsType", Description = "新聞", Seq = 0, Title = "新聞類型", ParentId = -1, }; _glossaryService.Add(newsType); _glossaryService.Add(new Glossary() { Key = "RecentNews", Value = "RecentNews", Description = "新聞類型", Seq = 1, Title = "最新活動", ParentId = newsType.Id, }); _glossaryService.Add(new Glossary() { Key = "UserActivity", Value = "UserActivity", Description = "新聞類型", Seq = 2, Title = "會員活動", ParentId = newsType.Id, }); _glossaryService.Add(new Glossary() { Key = "OnlineMarket", Value = "OnlineMarket", Description = "新聞類型", Seq = 3, Title = "在線商城", ParentId = newsType.Id, }); _glossaryService.Add(new Glossary() { Key = "AboutUs", Value = "AboutUs", Description = "新聞類型", Seq = 4, Title = "關於咱們", ParentId = newsType.Id, }); Repository.UnitOfWork.Commit();
而後從字段中讀出數據去添加菜單高併發
var newsMenu = Regist("新聞管理", "/portal/home/handerindex?menuId=" + systemManagerMenu.Identify + "-NewsType", systemManagerMenu.Identify, systemManagerMenu.Identify + "-NewsType"); //新聞管理 _glossaryService.GetFiltered("新聞類型").ForEach(item => { Regist(item.Title,string.Format( "/portal/news/index?typeid={0}&type={1}" , item.Id,item.Title), newsMenu.Identify, newsMenu.Identify + "-" + item.Id); });
加載三級菜單
/// <summary> /// 處理界面 /// </summary> /// <returns></returns> public ActionResult HanderIndex(string menuId) { ViewBag.Tree = string.Empty; //TODO:請求兩次,待處理 if (menuId == null) return View(); var items = _menuService.GetChildrenMenus(menuId); ViewBag.Tree = JsonConvert.SerializeObject(items); return View(); }
界面如圖
而後在Search和ViewModel中有一個字段是TypeId,這樣在List,PageSearch,AddOrEdit中就能夠自動綁定值了。
public class NewsSearch:SearchBase { public long? TypeId { get; set; } public string Title { get; set; } }
MVC默認的系統比較弱,當controller和view比較多的時候一個文件下面內容會很是多,我這裏作了一個模塊化處理.
using System.Web.Mvc; namespace Coralcode.Mvc.ViewEngines { public class ThemesRazorViewEngine : RazorViewEngine { public ThemesRazorViewEngine() { this.AreaViewLocationFormats = new string[3] { "~/Themes/{2}/{1}/{0}.cshtml", "~/Themes/Shared/{0}.cshtml", "~/Themes/{2}/Shared/{0}.cshtml" }; this.AreaMasterLocationFormats = new string[1] { "~/Themes/Shared/{0}.cshtml" }; this.AreaPartialViewLocationFormats = new string[4] { "~/Themes/{2}/{1}/{0}.cshtml", "~/Themes/{2}/Shared/{0}.cshtml", "~/Themes/Shared/{0}.cshtml", "~/Themes/Shared/Control/{0}.cshtml" }; this.ViewLocationFormats = new string[2] { "~/Themes/{1}/{0}.cshtml", "~/Themes/Shared/{0}.cshtml" }; this.MasterLocationFormats = new string[1] { "~/Themes/Shared/{0}.cshtml" }; this.PartialViewLocationFormats = new string[2] { "~/Themes/{1}/{0}.cshtml", "~/Themes/Shared/{0}.cshtml" }; } public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { if (controllerContext.RouteData.Values.ContainsKey("area") && !controllerContext.RouteData.DataTokens.ContainsKey("area")) controllerContext.RouteData.DataTokens.Add("area", controllerContext.RouteData.Values["area"]); return base.FindView(controllerContext, viewName, masterName, useCache); } }
項目目錄結構以下圖
這樣子發佈後的目錄很是的乾淨,如圖:
這部分在Controller設計那一節有詳細說明,請往前一步參考