Asp.net core mvc analysis: mvc Execution Process (1), coremvc
The preceding describes the routing process. Let's take a look at the MvcRouteHandler code:
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 ;}
Before giving a detailed introduction to the search logic of ActionDescriptor, let's take a look at what ActionDescriptor is and how it gets 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 foreach (var routeValueProvider in attributes must be equal to the value specified in the request. ofType <IRouteValueProvider> () {controllerModel. routeValues. add (routeValueProvider. routeKey, routeValueProvider. routeValue);} // api-related configuration va R 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
Return to the ActionSelector. SelectCandidates method and call the ActionSelectionDecisionTree. Select method. The 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. The DecisionTreeBuilder class is located in the Route application. By viewing the DecisionBuilder code, we find that an IClassifier is required for GenerateTree. In the mvc Framework, the implementation is ActionDescriptorClassifier. In the GetCriteria method, the corresponding IDictionary <string, DecisionCriterionValue>, this will exist as the branch of the tree when the search tree is generated. For details, see DecisionTreeBuilder <TItem>. generateTree will understand.
The search tree is available, ActionSelector. selectCandidates uses this tree to match the route data of the current request on the tree, and obtains all the actiondescriptors that meet the requirements. Then, it calls SelectBestCandidate to obtain the most qualified ActionDescriptor, if the last searched ActionDescriptor is not one, AmbiguousActionException is thrown.
Continue 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 ();};
First come here, next article, continue to analyze what has been done after invoker.