• ASP.NET MVC 源码分析(一)


    ASP.NET MVC 源码分析(一)

      直接上图:

      

      我们先来看Core的设计:

          

      从项目结构来看,asp.net.mvc.core有以下目录:

    ActionConstraints:action限制相关

    AntiForgery:防伪相关

    ActionResults:action返回对象相关

    ApiExplorer:API描述和元数据相关接口

    ApplicationModels:应用程序模型相关,应该是全局的model

    Areas:地区标签

    Filters:大名鼎鼎的过滤器组件

    Formatters:格式化相关的东东

    Internal:这个从名称看不出是做什么的,打开一看里面是一个路由决策树的实现

    ModelBinding:模型绑定,从request 对象取值映射到model的实现

    ParameterBinding: ModelBinding的上下文和模型更新入口

    Rendering:重量级选手,视图渲染逻辑都在这了

    Routing:路由控制相关

    ViewComponents:视图组件

    剩下的一些零闪的类大致就是controller,controllerFactoary和一些限定请求资源的标签的实现,OK 接下来让我们开始探究吧!

       面对一堆的类我们要从哪开始入手呢?对!我们要找到整个core的生命周期的主线,从主线入手解析相关组件设计,根据ASP.NET整个管道模型的设计(园子讲这个的文章很多,我就不熬述了),我们可以了解到,asp.net.mvc.core  实际上负责的是httphandler生命周期中做的一些事情,他从获取httpContext.Request 对象进行消息路由(Routing),请求过滤(filters,AntiForgery,ActionConstraints),参数映射(比如modelBinding,ParameterBinding),参数验证(比如modelValidate),逻辑处理,最后返回结果(ActionResults),对结果进行渲染输出到流(Rendering,ViewComponents)。

      我们从Microsoft.AspNet.Routing开始看:

      最高层的抽象IRouter,这个路由接口到底做了什么事情呢?

        public interface IRouter
        {
            Task RouteAsync(RouteContext context);
    
            VirtualPathData GetVirtualPath(VirtualPathContext context);
        }

      这个接口只有两个方法,一个是RouteAsync根据路由上下文解析路由到相应的处理器上,一个GetVirtualPath负责生成路由的URL,即URL重写asp.net 5为了平台的可移植性,把大部分组件都和system.web 解耦了,以前的版本route的抽象是超类RouteBase负责,包命为System.Web.Routing,废话不多说,上代码:

      public abstract class RouteBase
      {
        private bool _routeExistingFiles = true;
    
        /// <summary>
        /// Gets or sets a value that indicates whether ASP.NET routing should handle URLs that match an existing file.
        /// </summary>
        /// 
        /// <returns>
        /// true if ASP.NET routing handles all requests, even those that match an existing file; otherwise, false. The default value is false.
        /// </returns>
        public bool RouteExistingFiles
        {
          get
          {
            return this._routeExistingFiles;
          }
          set
          {
            this._routeExistingFiles = value;
          }
        }
    
        /// <summary>
        /// When overridden in a derived class, returns route information about the request.
        /// </summary>
        /// 
        /// <returns>
        /// An object that contains the values from the route definition if the route matches the current request, or null if the route does not match the request.
        /// </returns>
        /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
        public abstract RouteData GetRouteData(HttpContextBase httpContext);
    
        /// <summary>
        /// When overridden in a derived class, checks whether the route matches the specified values, and if so, generates a URL and retrieves information about the route.
        /// </summary>
        /// 
        /// <returns>
        /// An object that contains the generated URL and information about the route, or null if the route does not match <paramref name="values"/>.
        /// </returns>
        /// <param name="requestContext">An object that encapsulates information about the requested route.</param><param name="values">An object that contains the parameters for a route.</param>
        public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
      }

      从以上两段代码的区别我们可以看到,移植后的GetRouteData改成了异步的RouteAsync返回了一个task,两个方法的输入参数都构造了自己的上下文环境对象,不在直接从httpContext中操作。啰嗦一句,在.net中我们随处可以见这种基于context上下文的设计,这种基于上下文的设计有什么好处呢,分离关注点,管理环境对象的生命周期灰常的nice.

      打开RouteContext看一下这家伙到底做了些什么:

    namespace Microsoft.AspNet.Routing
    {
        public class RouteContext
        {
            private RouteData _routeData;
    
            public RouteContext(HttpContext httpContext)
            {
                HttpContext = httpContext;
    
                RouteData = new RouteData();
            }
    
            public HttpContext HttpContext { get; private set; }
    
            public bool IsHandled { get; set; }
    
            public RouteData RouteData
            {
                get
                {
                    return _routeData;
                }
                [param: NotNull]
                set
                {
                    _routeData = value;
                }
            }
        }
    }

    打开一看,尼玛,原来就是httpContext,routeData,IsHandled的一个组合,哈哈,在.net的设计中组合优于继承的思想基本烂大街,httpContext 就不说了,asp.net 生命周期的管控全靠它,isHand一个bool值的属性(我猜估计是指示路由是否完成之类的标识),routeData这个才是route组件独有的东东,看这个名字我们就知道他是一个数据结构,保存的是路由相关的信息:

      

            /// <summary>
            /// Creates a new <see cref="RouteData"/> instance with values copied from <paramref name="other"/>.
            /// </summary>
            /// <param name="other">The other <see cref="RouteData"/> instance to copy.</param>
            public RouteData([NotNull] RouteData other)
            {
                DataTokens = new Dictionary<string, object>(other.DataTokens, StringComparer.OrdinalIgnoreCase);
                Routers = new List<IRouter>(other.Routers);
                Values = new RouteValueDictionary(other.Values);
            }

    ([NotNull] 这个标签是最新的语法特性,同样体现的是一种声明式编程的思想)我们通过这个构造函数可以知道我们创建routeData的时候需要传入一些可能是路由规则配置的dictionary信息,具体他是怎么工作的,到时候看调用的时候就真相大白了。

      让我们回过头来看VirtualPathContext,由于重写呈现给客户端查看的URL地址肯定是发生在GetRouteData路由之后的,我们可以大胆的猜想他也是持有HttpContext对象和另一些route相关数据对象的组合,果不其然,他的实现没有让我们失望:

        public class VirtualPathContext
        {
            public VirtualPathContext(HttpContext httpContext,
                                      IDictionary<string, object> ambientValues,
                                      IDictionary<string, object> values)
                : this(httpContext, ambientValues, values, null)
            {
            }
    
            public VirtualPathContext(HttpContext context,
                                      IDictionary<string, object> ambientValues,
                                      IDictionary<string, object> values,
                                      string routeName)
            {
                Context = context;
                AmbientValues = ambientValues;
                Values = values;
                RouteName = routeName;
            }
    
            public string RouteName { get; private set; }
    
            public IDictionary<string, object> ProvidedValues { get; set; }
    
            public IDictionary<string, object> AmbientValues { get; private set; }
    
            public HttpContext Context { get; private set; }
    
            public bool IsBound { get; set; }
    
            public IDictionary<string, object> Values { get; private set; }
        }

    OK,IRouter的介绍到此为止。

     下一篇我们将开始介绍IRouteBuilder路由构建者接口,也是route组件的核心。

  • 相关阅读:
    [百度贴吧]飞腾1500a .VS. 龙芯3a3000: 同频实用性能对比
    内网环境安全整改简单办法
    PowerPoint储存此文件时发生错误 出现错误的问题解决方法
    灰阶
    Delphi实现窗体内嵌其他应用程序窗体
    access数据库用sql语句添加字段,修改字段,删除字段
    Bootstrap IIFE
    ZUI前段框架简介
    Win7去除桌面残影的方法
    Delphi中window消息截获的实现方式(2)
  • 原文地址:https://www.cnblogs.com/LiJianBlog/p/4554990.html
Copyright © 2020-2023  润新知