前面介绍了路由的过程,我们再来看下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之后做了什么。

  

  

asp.net core mvc剖析:mvc执行过程(一)的更多相关文章

  1. ASP.Net Core 2.2 MVC入门到基本使用系列 (三)

    本教程会对基本的.Net Core 进行一个大概的且不会太深入的讲解, 在您看完本系列之后, 能基本甚至熟练的使用.Net Core进行Web开发, 感受到.Net Core的魅力. 本教程知识点大体 ...

  2. ASP.NET Core 2.0 MVC项目实战

    一.前言 毕业后入职现在的公司快有一个月了,公司主要的产品用的是C/S架构,再加上自己现在还在学习维护很老的delphi项目,还是有很多不情愿的.之前实习时主要是做.NET的B/S架构的项目,主要还是 ...

  3. ASP.Net Core 2.2 MVC入门到基本使用系列 (四)

    本教程会对基本的.Net Core 进行一个大概的且不会太深入的讲解, 在您看完本系列之后, 能基本甚至熟练的使用.Net Core进行Web开发, 感受到.Net Core的魅力. 本教程知识点大体 ...

  4. asp.net core 3.0 MVC JSON 全局配置

    asp.net core 3.0 MVC JSON 全局配置 System.Text.Json(default) startup配置代码如下: using System.Text.Encodings. ...

  5. 在 Asp.Net Core 中安装 MVC

    在 ASP.NET Core 中安装 MVC 到目前为止,我们在本系列视频中使用的 ASP.NET Core 项目是使用“空”项目模板生成的.目前这个项目没有设置和安装 MVC. 两个步骤学会在 AS ...

  6. ASP.NET CORE 1.0 MVC API 文档用 SWASHBUCKLE SWAGGER实现

    from:https://damienbod.com/2015/12/13/asp-net-5-mvc-6-api-documentation-using-swagger/ 代码生成工具: https ...

  7. ASP.Net Core 2.2 MVC入门到基本使用系列 (二)

    本教程会对基本的.Net Core 进行一个大概的且不会太深入的讲解, 在您看完本系列之后, 能基本甚至熟练的使用.Net Core进行Web开发, 感受到.Net Core的魅力. 本教程知识点大体 ...

  8. ASP.Net Core 2.2 MVC入门到基本使用系列 (一)

    本教程会对基本的.Net Core 进行一个大概的且不会太深入的讲解, 在您看完本系列之后, 能基本甚至熟练的使用.Net Core进行Web开发, 感受到.Net Core的魅力. 本教程知识点大体 ...

  9. ASP.Net Core 2.2 MVC入门到基本使用系列 (三)(转)

    本教程会对基本的.Net Core 进行一个大概的且不会太深入的讲解, 在您看完本系列之后, 能基本甚至熟练的使用.Net Core进行Web开发, 感受到.Net Core的魅力. 本教程知识点大体 ...

  10. ASP.NET Core 2.0 MVC「远程」验证

    问题 如何 在ASP.NET Core MVC中使用[Remote]属性来实现模型验证 . 解 在 启动时, 为MVC配置中间件和服务. 添加一个模型. 添加一个控制器. 为jQuery添加一个Raz ...

随机推荐

  1. iOS中UITextField 使用全面解析

    //初始化textfield并设置位置及大小   UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 13 ...

  2. sqlserver merge into

    create table #ttt(id int,name nvarchar(10));merge into #ttt t using (select 1 as id ,'eee' as name ) ...

  3. git 关联远程库(https协议)

    1.在oschina上新建库 2.在本地文件夹右键->"git Bash here" 3.设置全局变量: git config --global user.name &quo ...

  4. Android与JNI(一) ---- Java调用C 静态调用

    第一.通过eclipse新建一个工程名为HelloJni的android工程,并编译. 第二.右键工程-->Android Tools --> Add Native Support,出现如 ...

  5. p便签,去掉首行缩进

    <p>fdsfdsfs</p> 使用P标签是,会自动的加上首行缩进,如果想去掉首行缩进,可以使用text-indent属性 <p style="text-ind ...

  6. Android滚动选择控件

    现在觉得github特别方便,我一般直接使用github中的内容, https://github.com/wangjiegulu/WheelView 这里面都有详细的介绍

  7. jQuery初始化

    jQuery 初始化的理解 var jQuery = function( selector, context ) { // The jQuery object is actually just the ...

  8. 使用(Drawable)资源——LayerDrawable资源

    与StateListDrawable有点类似,LayerDrawable也可包含一个Drawable数组,因此系统将会按这些Drawable对象的数组顺序来绘制它们,索引最大的Drawable对象将会 ...

  9. Apache自带压力测试工具ab用法简介

    ab命令原理 ab命令会创建很多的并发访问线程,模拟多个访问者同时对某一URL进行访问.它的测试目标是基于URL的,因此,既可以用来测试Apache的负载压力,也可以测试nginx.lighthttp ...

  10. 算法一之N皇后问题

    (写这篇文章主要是明天就要考试了,算法考试,今天不想再复习了,xiang着今天也开通了博客,于是在这个平台上进行复习,应该会更高效.最后祝愿我明天考个好成绩.嘻嘻...) n皇后问题,主要是应用到回溯 ...