前面介绍了路由的过程,我们再来看下MvcRouteHandler的代码:
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; }
在详细介绍ActionDescriptor查找逻辑前,我们得先来看下ActionDescriptor是什么,它是如何得到的?
我们从IActionSelector.SelectCandidates开始进行跟踪,并根据MvcCoreServiceCollectionExtensions提供的AddMvcCoreServices的依赖注入配置,我们不难得到下面的调用关系:
在DefaultApplicationModelProvider.OnProvidersExecuting方法中通过反射方式解析程序集中的类信息。
protected virtual ControllerModel CreateControllerModel(TypeInfo typeInfo) { 。。。。。。 //获取RouteAttribute特性信息,这里是采用循环的方式,直到找到第一个定义了IRouteTemplateProvider特性的类为止 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>()); //获取路由规则数据,要求请求时某个路由数据必须等于设置的值 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
再回到ActionSelector.SelectCandidates方法,在这里面调用ActionSelectionDecisionTree.Select方法,代码如下:
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生成的一个查找树。DecisionTreeBuilder类位于Route应用程序中。通过查看DecisionBuilder的代码,我们发现在GenerateTree时需要一个IClassifier,在mvc框架中的实现是ActionDescriptorClassifier,GetCriteria方法中就是循环RouteValue生成对应的IDictionary<string, DecisionCriterionValue>,这个在生成查找树时,会作为树的分支存在,大家可以查看DecisionTreeBuilder<TItem>.GenerateTree就会理解。
查找树有了,ActionSelector.SelectCandidates就使用这个树根据当前请求的路由数据在树上进行匹配,会得到所有符合要求的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(); };
先到这里,下篇文章,继续分析invoker之后做了什么。