前面介绍了路由的过程,我们再来看下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. svn 几个好用的命令

    Mac下操作的命令 1. 删除目录及子目录下,未添加的文件 svn status . | grep '^?' | awk '{print $2}' | xargs rm -rf 2.恢复根目录及子目录 ...

  2. C#的逆变和协变

    out: 输出(作为结果),in:输入(作为参数) 所以如果有一个泛型参数标记为out,则代表它是用来输出的,只能作为结果返回,而如果有一个泛型参数标记为in,则代表它是用来输入的,也就是它只能作为参 ...

  3. HTTP协议快速入门

    一.定义 The Hypertext Transfer Protocol (HTTP) is an application protocol for distributed, collaborativ ...

  4. [Angular Tutorial] 10 -More Templating

    在这一步中,我们会实现电话细节的视图,这在用户点击列表中的一部电话时被展示. ·当您点击列表中的一部电话时,带有电话特定信息的电话细节页面将被展示. 我们打算使用$http来获取我们的数据,以此来实现 ...

  5. Mysql和Oracle的一些语法区别

    作为一个有追求的程序猿,当然要不断的学习,巴拉巴拉巴拉...好了,贴一个网址给大家,哈哈 MySQL与Oracle 差异比较:http://www.cnblogs.com/HondaHsu/p/364 ...

  6. dubbo框架揭秘之服务发布

    通常情况下是通过Spring配置的方式去实现服务的发布,为了方便调试,我就不采用Spring配置的方式. DemoService demo = new DemoServiceImpl(); Appli ...

  7. Sed替换 内容带反斜杠(/)

    sed "s#XXXX#${NAME}#" $MAIL_CONTENT > /tmp/MAIL_CONTENT1.tmp -----不论什么字符,紧跟着s命令的都被认为是新的 ...

  8. 如何解决Ajax跨域问题-1

    如何解决Ajax跨域问题 最近在做AJAX调用C的问题,出现跨域问题,学习总结如下: 在做ajax读取数据的时候,经常会遇到ajax需要跨域的问题,但由于浏览器安全方面的限制,XMLHttpReque ...

  9. box-sizing

    Over the generations, people realized that math is not fun, so a new CSS property called box-sizing ...

  10. 屏蔽ps联网激活

    屏蔽ps联网激活C:\Windows\system32\drivers\etc\host127.0.0.1 lm.licenses.adobe.com127.0.0.1 na1r.services.a ...