MVC execution Process
Extension of the route
I understand that the routing function has the following several
- SEO optimization, with "/" Separate URL crawler more love to eat
- Separation of physical and logical files, URLs are no longer mapped by file path
- The choice of controller,action
Extension of the MVC route
To tell the truth about MVC's routing I seldom do extensions, and in the MVC4 era, I rewrite the case of the URL, and after MVC5, MVC comes with the configuration to lowercase the URL. But there's a configuration that has to be mentioned. That is area, and after your system reaches a certain size, it will be easier for controllers to manage it through area. This gives me the area extension, very simple but important, notice that the subclass must end with Arearegistration, and also follow the Convention is limited to the principle of configuration, of course you can rewrite.
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}} );}}
Extension of the WEBAPI route
The above MVC route also registered the route of the API, of course, there is a better way, because WEBAPI, basically do not need url.action and htmlaction, this route out of the stack of policy, do not need to go through the route to generate the URL, so
We only need to do the parsing of the route, and Webapi does not provide the own area mechanism, so I expanded the defaulthttpcontrollerselector, get to the route area and controller parameters
and then complete the stitching, Then the reflection type lookup, at the beginning of the pre-cache all controllers, so the performance is good, because the original code is anti-compilation obtained, so the LINQ part is a bit bad, and so I back to the company to get the source code and then modify.
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 Strin G 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) {thi s._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.gethttpcontr Ollertyperesolver (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, con Trollertype); }} return base. Selectcontroller (Request); } }}
Controller activation
Controller activation This is the core of the integration of MVC and IOC, with three parts of the IOC
- Register type of discovery and registration, solve how to find those types that need to be registered and how to register, here I use attribute way to find, with Unity's own registration method to register
- Resolve types of parsing, how to know a type and instantiate it
- Lifttime How to control the life cycle of an instance, such as whether to use a singleton, the life cycle also uses Unity's own, mainly used in the single case and each resolution is an instance
MVC needs and IOC activation The first thing we focus on is where to register and where to instantiate, because MVC uses a contract that takes precedence over configuration, all controllers end with "controller", so here I directly load
Assembly, and then find all types to the end of the controller to register just fine, activate the word has the following three ways, the difficulty of the degree of increase in order, nesting depth is also incremented, here I use rewrite
The default defaultcontrollerfactory way to activate, so that you can use their own and other mechanisms to achieve, other ways you can expand themselves.
- Controllerfactory
- Icontrolleractivetor
- Idependencyresolver
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 reques Tcontext, 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 activates the following 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 control Lerdescriptor, Type controllertype) {Ihttpcontroller Httpcontroller = (ihttpcontroller) unityservice.resolve (cont Rollertype); Httprequestmessageextensions.registerfordispose (Request, Httpcontroller as IDisposable); return httpcontroller; } }}
Filter Execution Logfilter
Here is basically the source code of MVC, just added a log function, grilled MVC source code Everyone must try
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 Onexceptio N (exceptioncontext filtercontext) {if (Filtercontext = = null) throw new ArgumentNullException ("Filterconte XT "); 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) fi ltercontext.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 viewdatadictionaryResultfilterHere the unified processing of the AJAX request return data to Resultmessage return, if not Jsonresult select Ignore, specifically designed to look at the controller design that section.
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);}}}
Action executionTo improve the concurrency of the weapon, the best way to use, here is a detailed introduction can be combined with Artec controller synchronization and asynchronous this section, which I personally recommend is the use of return task<actionresult>.
Valueprovider and ModelbinderThe parameters in the URL are used as the data source for the model binding. When you do a level three menu, you can use it, for example, I first add the following data to the data dictionary
var newstype = new Glossary () {Key = "Newstype", Value = "Newstype", Description = "News", Seq = 0, Title = "News Type", ParentID =-1,}; _glossaryservice.add (Newstype); _glossaryservice.add (New Glossary () {Key = "recentnews", Value = "Recentnews", Description = "News Type", Seq = 1, Title = "latest Activity", ParentID = Newstype.id, }); _glossaryservice.add (New Glossary () {Key = "useractivity", Value = "Useractivity", Description = "News Type", Seq = 2, Title = "Member activity", ParentID = Newstype . Id,}); _glossaryservice.add (New Glossary () {Key = "Onlinemarket", Value = "Onlinemarket", Description = "News Type", Seq = 3, Title = "Online Mall", ParentID = Newstype.id,}); _glossaryservice.add (New Glossary () {Key = "AboutUs", Value = "AboutUs", Description = "News Type", Seq = 4, Title = "About Us", ParentID = Newstype.id, }); Repository.UnitOfWork.Commit ();
Then read the data from the field to add the menu
var newsmenu = regist ("News Management", "/portal/home/handerindex?menuid=" + systemmanagermenu.identify + "-newstype", Systemmanagermenu.identify, systemmanagermenu.identify + "-newstype"); News management _glossaryservice.getfiltered ("News type"). ForEach (item = { regist (item). Title,string. Format ("/portal/news/index?typeid={0}&type={1}", item. Id,item. Title), newsmenu.identify, newsmenu.identify + "-" + item. (Id); });
Load level Three Menu
<summary>///processing interface///</summary>// <returns></returns> Public ActionResult Handerindex (String menuId) { Viewbag.tree = string. Empty; TODO: Request two times, pending if (menuId = = null) return View (); var items = _menuservice.getchildrenmenus (menuId); Viewbag.tree = jsonconvert.serializeobject (items); return View ();
Interface
Then there is a field in search and ViewModel that is typeid so that the value can be automatically bound in List,pagesearch,addoredit.
Public class Newssearch:searchbase {public long? TypeId {get; set;} public string Title {get; set;} }
View Discovery and ActionResult executionMVC default system is weak, when the controller and view more when a file under a lot of content, I have done a modular processing.
Using System.web.mvc;namespace coralcode.mvc.viewengines{public class Themesrazorviewengine:razorviewengine {Publ IC 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, bo Ol 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); } }
Project directory structure such as
The post-release catalog is very clean,
Jsonresult execution and metadata (model metadata delivery mechanism)This section is described in detail in the Controller design section, please refer to the next step
Summarize
- Everyone must look at the MVC source code,
- I want to be able to string the MVC execution process in my head.
- The main body design has been finished, source code collation, work relatively busy, forgive me,
- Like please concern, there are questions please leave a message, a headache, sleep, good night
MVC extended Design for all-stack programming architecture of CRUD