前面介绍了路由的过程,我们再来看下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. 【转】15款Java程序员必备的开发工具

    如果你是一名Web开发人员,那么用膝盖想也知道你的职业生涯大部分将使用Java而度过.这是一款商业级的编程语言,我们没有办法不接触它. 对于Java,有两种截然不同的观点:一种认为Java是最简单功能 ...

  2. bzoj-1834 network 网络扩容 【网络流】

    这题就是复习下网络流. #include <bits/stdc++.h> #define rep(i, a, b) for (int i = a; i <= b; i++) #def ...

  3. Thinking in scala (6)----高阶函数----返回一个函数

    在Thinking in scala (5)----高阶函数* 里面,我们演示了如何把一个函数作为参数传递给另外一个函数. 在本文里面,我们来演示函数式编程另外一个重要的特性:返回一个函数.首先来看这 ...

  4. Android与JNI(二) ---- Java调用C++ 动态调用

    目录: 1. 简介 2. JNI 组件的入口函数 3. 使用 registerNativeMethods 方法 4. 测试 5. JNI 帮助方法 6. 参考资料 1. 简介 Android与JNI( ...

  5. sqlite3API函数

    回顾: DDL 表的创建.修改.删除 create table 表名(字段名 字段类型 [约束],...); alter table 表名 {rename to 新名字 | add column 字段 ...

  6. App外包开发周期一般多长?

    很多人问我,开发一个app要用多长时间.事实上开发一款app没有固定周期的,得因产品而论,你软件的功能需求决定了app外包开发的周期.但是除了app本身以外,人为因素往往对开发周期也有一定的影响.例如 ...

  7. R语言中的if-else语句写法

    结构  1 :  if()  xx  else    yy    一行: 结构  2:   if()  {xx} else  {yy} 或者   if(){ xx }else    #此处不能两行写 ...

  8. iconfont.cn阿里巴巴矢量图下载字体图标实战

    1.阿里巴巴矢量图网址:www.iconfont.cn 2.然后用新浪微博账号登录 3.输入要查找的图标相应的关键字,回车 4.滑过要找的图标,点击购物车,让图标存储到暂存架中 5.点击暂存架,存储为 ...

  9. MySQL生产库全库备份脚本

    创建一个单独的备份用户backup,不要用root 创建备份目录 :mkdir -p /databackup/fullbackup mysql> grant SELECT,RELOAD,SHOW ...

  10. #图# #dijkstra# ----- OpenJudge 726:ROADS

    OpenJudge 726:ROADS 总时间限制: 1000ms内存限制: 65536kB 描述 N cities named with numbers 1 ... N are connected ...