Asp.net core mvc analysis: mvc action selection, coremvc

Source: Internet
Author: User

Asp.net core mvc analysis: mvc action selection, coremvc

After an http request comes in, first find the most qualified IRouter by matching the routing rules, and then call IRouter. routeAsync to set RouteContext. handler, and finally give the request to RouteContext. handler. Two IRouter implementations are provided in MVC:

1. MvcAttributeRouteHandler

2. MvcRouteHandler

Let's take a look at the implementation logic of UseMvc.

Public static IApplicationBuilder UseMvc (this IApplicationBuilder app, Action <IRouteBuilder> assumeroutes ){...... // Instantiate the route constructor var routes = new RouteBuilder (app) {// set the default processor, that is, MvcRouteHandler is used to process the request DefaultHandler = app when the route meets the conditions. applicationServices. getRequiredService <MvcRouteHandler> (),}; // configure the routing rule assumeroutes (routes); // This sentence is very important. The global routing rule configured above, we can also use RouteAttribute to configure routing rules on the Controller or controller method. These rules take precedence over routes. routes. insert (0, AttributeRouting. createAttributeMegaRoute (app. applicationServices); // routes. the Build method generates the IRouter object. Later we will look at the details and register a RouterMiddleware middleware return app through UseRouter. useRouter (routes. build ());}

  

The above assumeroutes (routes) Statement registration Route is associated with MvcRouteHandler, while routes. routes. insert (0, AttributeRouting. createAttributeMegaRoute (app. (ApplicationServices) register the mvbuteroute associated with MvcAttributeRouteHandler, and the latter is preferentially selected.

One of the most important tasks in the two RouterHanlder jobs is to select actions. The code for MvcRouterHandler is as follows:

Public Task RouteAsync (RouteContext context ){...... // Search for the set of compliant ActionDescriptor var candidates = _ actionSelector Based on the route information. selectCandidates (context); if (candidates = null | candidates. count = 0) {_ logger. noActionsMatched (context. routeData. values); return TaskCache. completedTask;} // select the most compliant ActionDescriptor var actionDescriptor = _ actionSelector according to the constraints. selectBestCandidate (context, candidates); if (actionDescriptor = null) {_ logger. noActionsMatc Hed (context. routeData. values); return TaskCache. completedTask;} context. handler = (c) => {var routeData = c. getRouteData (); var actionContext = new ActionContext (context. httpContext, routeData, actionDescriptor); if (_ actionContextAccessor! = Null) {_ actionContextAccessor. actionContext = actionContext;} var invoker = _ actionInvokerFactory. createInvoker (actionContext); if (invoker = null) {throw new InvalidOperationException (Resources. formatActionInvokerFactory_CouldNotCreateInvoker (actionDescriptor. displayName);} return invoker. invokeAsync () ;}; return TaskCache. completedTask ;}

Here, an ActionSelector is used to select actions based on the route data to obtain the ActionDescriptor that meets the requirements. What is ActionDescriptor? It is a description class of an action, including the action name and constraints. How does it get it?

We started tracking from IActionSelector. SelectCandidates and got the following call relationship according to the dependency injection configuration of AddMvcCoreServices provided by MvcCoreServiceCollectionExtensions:

In the defaapplapplicationmodelprovider. OnProvidersExecuting method, class information in the Assembly is parsed through reflection. Let's analyze the controller-Class Analysis Code, which is in the CreateControllerModel method.

Protected virtual ControllerModel CreateControllerModel (TypeInfo typeInfo ){...... // Obtain RouteAttribute information. This is a loop until the first class that defines the IRouteTemplateProvider feature is found. Therefore, if RouteAttribute is not configured for the subclass, IRouteTemplateProvider [] routeAttributes = null; do {routeAttributes = currentTypeInfo. getCustomAttributes (inherit: false ). ofType <IRouteTemplateProvider> (). toArray (); if (routeAttributes. length> 0) {// Found 1 or more route attributes. break;} currentTypeInfo = currentTypeInfo. baseTyp E. GetTypeInfo ();} while (currentTypeInfo! = ObjectTypeInfo );...... Var controllerModel = new ControllerModel (typeInfo, attributes); // create Selectors. This requires AddRange (controllerModel. selectors, CreateSelectors (attributes); // obtain the Controller name controllerModel. controllerName = typeInfo. name. endsWith ("Controller", StringComparison. ordinalIgnoreCase )? TypeInfo. name. substring (0, typeInfo. name. length-"Controller ". length): typeInfo. name; // Add the filter feature to the AddRange (controllerModel. filters, attributes. ofType <IFilterMetadata> (); // obtain the route rule data. The value of a route data must be equal to the value set during the request, for example, if [Area ("test")] is set on the controller, the current action is used only when the route data contains area and the value is equal to test, of course, you can customize some limitations of foreach (var routeValueProvider in attributes. ofType <IRouteValueProvider> () {controllerModel. routeValues. add (ro UteValueProvider. RouteKey, routeValueProvider. RouteValue);} // api-related configuration var apiVisibility = attributes. OfType <IApiDescriptionVisibilityProvider> (). FirstOrDefault (); if (apiVisibility! = Null) {controllerModel. ApiExplorer. IsVisible =! ApiVisibility. IgnoreApi;} var apiGroupName = attributes. OfType <IApiDescriptionGroupNameProvider> (). FirstOrDefault (); if (apiGroupName! = Null) {controllerModel. apiExplorer. groupName = apiGroupName. groupName;} // whether the analysis controller implements the action filter and result filter interfaces. If we need to implement a special action filter or result filter for a controller, you don't need to create a filter feature class separately, so you can directly implement the interface of the controller. This facilitates if (typeof (IAsyncActionFilter ). getTypeInfo (). isAssignableFrom (typeInfo) | typeof (IActionFilter ). getTypeInfo (). isAssignableFrom (typeInfo) {controllerModel. filters. add (new ControllerActionFilter ();} if (typeof (IAsyncResultFilter ). getTypeInfo (). isAssignableFrom (typeInfo) | typeof (IResultFilter ). getTypeInfo (). isAssignableFrom (typeInfo) {controllerModel. filters. add (new ControllerResultFilter ();} return controllerModel ;}

  

After the above method is completed, a ControllerModel object is obtained. The CreateActionModel method creates an ActionModel object. The analysis process is similar to the CreateControllerModel method, so we will not introduce it again, finally, the following hierarchical relationship objects are obtained:

ApplicationModel-> ControllerModel-> ActionModel

After obtaining the final ApplicationModel, The ControllerActionDescriptorBuilder. Build (applicationModel) will generate the corresponding ControllerActionDescriptor set.

Return to the ActionSelector. SelectCandidates method. In this method, call ActionSelectionDecisionTree. Select to Select the ActionDescriptor that meets the requirements. The implementation code is as follows:

 public IReadOnlyList<ActionDescriptor> Select(IDictionary<string, object> routeValues)        {            var results = new List<ActionDescriptor>();            Walk(results, routeValues, _root);            return results;        }

_ Root is a type of DecisionTreeNode <ActionDescriptor>, which is a search tree generated by DecisionTreeBuilder <ActionDescriptor>. GenerateTree.

DecisionTreeNode is defined as follows:

Internal class DecisionTreeNode <TItem >{// set of qualified ActionDescriptor public IList <TItem> Matches {get; set ;} // branch rule public IList <DecisionCriterion <TItem> Criteria {get; set ;}}

A branch rule is defined as follows:

internal class DecisionCriterion<TItem>    {        public string Key { get; set; }        public Dictionary<object, DecisionTreeNode<TItem>> Branches { get; set; }    }

Search logic:

1. Check whether Matches exists on the current node. If Matches exists in the search result set.

2. Obtain the branch rule key by repeating the branch rule, obtain the value of the corresponding key from the route data according to the key, and find the corresponding DecisionTreeNode from the Branches dictionary using this value. <TItem>

3. Execute Step 1 and Step 2 on the branch Node.

4. Return the final search result.

 

Through the above steps, we will get all the qualified ActionDescriptor, and then call SelectBestCandidate to obtain the most qualified ActionDescriptor. If the last searched ActionDescriptor is not one, an AmbiguousActionException is reported.

Return to MvcRouteHandler. After finding the ActionDescriptor, set context. Handler.

Context. handler = (c) => {var routeData = c. getRouteData (); // instantiate the ActionContext object var actionContext = new ActionContext (context. httpContext, routeData, actionDescriptor); if (_ actionContextAccessor! = Null) {_ actionContextAccessor. actionContext = actionContext;} // create IActionInvoker var invoker = _ actionInvokerFactory. createInvoker (actionContext); if (invoker = null) {throw new InvalidOperationException (Resources. formatActionInvokerFactory_CouldNotCreateInvoker (actionDescriptor. displayName);} // execute the invoker to process the request return invoker. invokeAsync ();};

In Hander, The ActionContext is instantiated Based on the found ActionDescriptor. Then, the invoker is created through ActionInvokerFactory, and the corresponding action of ActionDescriptor is executed through invoker, and the result is returned.

Here, we will continue to introduce Invoker content.

  

  

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.