asp.net core mvc剖析:mvc动作选择
一个http请求过来后,首先经过路由规则的匹配,找到最符合条件的的IRouter,然后调用IRouter.RouteAsync来设置RouteContext.Handler,最后把请求交给RouteContext.Handler来处理。在MVC中提供了两个IRouter实现,分别如下:
1,MvcAttributeRouteHandler
2,MvcRouteHandler
我们再来看一下UseMvc的实现逻辑
public static IApplicationBuilder UseMvc( this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes) { 。。。。。。 //实例化路由构造器 var routes = new RouteBuilder(app) { //设置默认处理器,就是路由符合条件时使用MvcRouteHandler来处理请求 DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(), }; //配置路由规则 configureRoutes(routes); //这句很重要,上面配置的全局的路由规则,我们同样可以在控制器或者控制器方法上使用RouteAttribute配置路由规则,这些规则会优先采用 routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices)); //routes.Build方法生成IRouter对象,一会我们在看具体细节,然后通过UseRouter注册一个RouterMiddleware中间件 return app.UseRouter(routes.Build()); }
上面的configureRoutes(routes)语句注册Route关联上了MvcRouteHandler,而routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices))注册AttributeRoute关联上了MvcAttributeRouteHandler,并且后者优先被匹配选择。
在这两个RouterHanlder里做的一个最重要的工作就是进行动作选择,以MvcRouterHandler为例,代码如下:
public Task RouteAsync(RouteContext context) { 。。。。。。 //根据路由信息查找符合要求的ActionDescriptor集合 var candidates = _actionSelector.SelectCandidates(context); if (candidates == null || candidates.Count == 0) { _logger.NoActionsMatched(context.RouteData.Values); return TaskCache.CompletedTask; } //按照约束规则选择最符合要求的一个ActionDescriptor var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates); if (actionDescriptor == null) { _logger.NoActionsMatched(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; }
在这里面借助一个ActionSelector来根据路由数据进行动作选择,得到符合要求的ActionDescriptor。ActionDescriptor是什么?它是一个动作的描述类,包含动作名称,约束条件等。它是如何得到的?
我们从IActionSelector.SelectCandidates开始进行跟踪,并根据MvcCoreServiceCollectionExtensions提供的AddMvcCoreServices的依赖注入配置,我们不难得到下面的调用关系:
在DefaultApplicationModelProvider.OnProvidersExecuting方法中通过反射方式解析程序集中的类信息。我们分析下控制器类分析的代码,这部分代码在CreateControllerModel方法中
protected virtual ControllerModel CreateControllerModel(TypeInfo typeInfo) { 。。。。。。 //获取RouteAttribute特性信息,这里是采用循环的方式,直到找到第一个定义了IRouteTemplateProvider特性的类为止,所以如果子类没有配置RouteAttribute,就会采用父类的配置 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.BaseType.GetTypeInfo(); } while (currentTypeInfo != objectTypeInfo); 。。。。。。 var controllerModel = new ControllerModel(typeInfo, attributes); //创建Selectors,这个要在查找ActionDescriptor时使用 AddRange(controllerModel.Selectors, CreateSelectors(attributes)); //获取控制器名称 controllerModel.ControllerName = typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ? typeInfo.Name.Substring(0, typeInfo.Name.Length - "Controller".Length) : typeInfo.Name; //把过滤器特性加入到Filters集合中 AddRange(controllerModel.Filters, attributes.OfType<IFilterMetadata>()); //获取路由规则数据,要求请求时某个路由数据必须等于设置的值,比如在控制器上设置[Area("test")],那只有当路由数据中包含了area且值等于test才用当前这个动作处理,当然大家可以自定义一些限制 foreach (var routeValueProvider in attributes.OfType<IRouteValueProvider>()) { controllerModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue); } //api相关配置 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; } // 分析控制器是否实现了动作过滤器和结果过滤器接口,如果我们需要对一个控制器实现一个特殊的动作过滤器或结果过滤器,就不用再单独创建过滤器特性类了,直接让控制器实现接口即可,这个很方便 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; }
上面的方法结束后,就得到了一个ControllerModel对象,CreateActionModel方法是创建ActionModel对象,分析过程基本跟CreateControllerModel方法类似,就不再介绍,结果分析后,最后得到了下面的层次关系对象:
ApplicationModel->ControllerModel->ActionModel
得到最终的ApplicationModel后,再有ControllerActionDescriptorBuilder.Build(applicationModel)生成对应的ControllerActionDescriptor集合。
再回到ActionSelector.SelectCandidates方法,在这个方法里面通过调用ActionSelectionDecisionTree.Select方法来选择符合要求的ActionDescriptor,实现代码如下:
public IReadOnlyList<ActionDescriptor> Select(IDictionary<string, object> routeValues) { var results = new List<ActionDescriptor>(); Walk(results, routeValues, _root); return results; }
_root是一个DecisionTreeNode<ActionDescriptor>类型,它是通过DecisionTreeBuilder<ActionDescriptor>.GenerateTree生成的一个查找树。
DecisionTreeNode定义如下:
internal class DecisionTreeNode<TItem> { //符合条件的ActionDescriptor集合 public IList<TItem> Matches { get; set; } // 分支规则 public IList<DecisionCriterion<TItem>> Criteria { get; set; } }
一个分支规则定义如下:
internal class DecisionCriterion<TItem> { public string Key { get; set; } public Dictionary<object, DecisionTreeNode<TItem>> Branches { get; set; } }
查找逻辑:
1,判断当前结点上是否存在Matches,如果存在放到查找结果集里。
2,循环分支规则,获取分支规则key,按照key从路由数据中获取对应key的值,在通过这个值从Branches字典中查找对应DecisionTreeNode<TItem>
3,在分支Node上执行1,2步
4,返回最后的查找结果
通过上面的步骤会得到所有符合要求的ActionDescriptor,然后调用SelectBestCandidate获取最符合条件的ActionDescriptor,如果最后查找到的ActionDescriptor不是一个,则报AmbiguousActionException异常。
回到MvcRouteHandler,在查找到ActionDescriptor之后,就设置context.Handler
context.Handler = (c) => { var routeData = c.GetRouteData(); //根据actiondescriptor实例化ActionContext对象 var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor); if (_actionContextAccessor != null) { _actionContextAccessor.ActionContext = actionContext; } //创建IActionInvoker var invoker = _actionInvokerFactory.CreateInvoker(actionContext); if (invoker == null) { throw new InvalidOperationException( Resources.FormatActionInvokerFactory_CouldNotCreateInvoker( actionDescriptor.DisplayName)); } //执行invoker处理请求 return invoker.InvokeAsync(); };
在Hander中,根据查找到的ActionDescriptor实例化ActionContext,然后通过ActionInvokerFactory创建invoker,通过invoker执行ActionDescriptor对应的动作,并返回结果。
先到这里,下面再继续介绍Invoker相关内容。