zoukankan      html  css  js  c++  java
  • Asp.Net Mvc5 之Controller

        经过前面介绍了路由系统之后,我们知道任何一个请求在经过asp.net url路由系统的拦截之后,会生成以controller/action 名称为核心的路由数据。asp.net mvc 根据此解析出目标controller 的类型,并最终激活具体的controller实例处理当前请求。

       为了解释从请求到执行目标controller的过程,我们调式源码(具体怎么调试源码可以参照我这篇文章 ),在MvcHandler里面的IHttpAsyncHandler.BeginProcessRequest处设置断点

    IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
      return BeginProcessRequest(context, cb, extraData);
    }

    大家都知道,asp.net运行机制是管道模型,asp.net mvc也同样遵循管道模型,查看MvcHandler代码,可以看出MvcHandler实现了接口IHttpHandler。此时我们继续深挖代码,找到下面代码片段

    protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
    {
      IController controller;
      IControllerFactory factory;
      ProcessRequestInit(httpContext, out controller, out factory);
    
      IAsyncController asyncController = controller as IAsyncController;
      if (asyncController != null)
      {
        //to do   }   
    else   {
        //to do   } }

    此处代码便是创建了Controller,也正是这篇文章所要阐述的主题。

      此时,再进一步分析“BeginProcessRequest”这个方法,阅读前三行代码,我们大概可以得知Controller 是在这个方法中创建的。那么在这个方法中找到“ProcessRequestInit”此方法,其中后面两个参数“controller”,“factory”都是带有前缀“out”的,也就是说两参数是通过引用来传递值。那么我们暂且不谈“BeginProcessRequest”在获取“IController”实例 、“IControllerFactory”实例之后会做什么后续处理,直接调试进入方法“ProcessRequestInit”,看它里面到底发生了什么,我们将“ProcessRequestInit”的方法实现贴出来

    private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
    {
      // If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks
      // at Request.Form) to work correctly without triggering full validation.
      // Tolerate null HttpContext for testing.
      HttpContext currentContext = HttpContext.Current;
      if (currentContext != null)
      {
          bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(currentContext);
          if (isRequestValidationEnabled == true)
          {
              ValidationUtility.EnableDynamicValidation(currentContext);
          }
      }
    
      AddVersionHeader(httpContext);
      RemoveOptionalRoutingParameters();
    
      // Get the controller type
      string controllerName = RequestContext.RouteData.GetRequiredString("controller");
    
      // Instantiate the controller and call Execute
      factory = ControllerBuilder.GetControllerFactory();
      controller = factory.CreateController(RequestContext, controllerName);
      if (controller == null)
      {
          throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            MvcResources.ControllerBuilder_FactoryReturnedNull,
                            factory.GetType(),
                            controllerName));
      }
    }

    在这个方法里面,我们将注意力集中在这两句注释“Get the controller type”、“Instantiate the controller and call Execute”所对应的代码。

      首先,程序先获取到控制的名称。我们在阅读Mvc的源码的时候,可以发现获取控制器的名称有两种方式,至于为何没有统一写法,我也不知道,虽然结果都是一样的。

    RequestContext.RouteData.GetRequiredString("controller");
    (string)requestContext.RouteData.Values["controller"];

    那么在获取到了控制器的名称之后,我们大胆猜想一下,获取控制器的实例是不是运用了反射了?

      然后,在获取到了控制器名称之后,程序就会执行实例化控制器并且调用。在这一步中涉及到了两个关键的类“ControllerBuilder”、实现IControllerFactory的类“DefaultControllerFactory”,此时我们继续调试下面那一行代码

    factory = ControllerBuilder.GetControllerFactory();

    通过字面上应该可以猜出是通过“ControllerBuilder”获取控制器工厂。ControllerBuilder属性是定义在Mvchandler中,通过属性注入ControllerBuilder对象,此时调试进入了ControllerBuilder内部,我们看一下GetControllerFactory()的实现细节

    private static ControllerBuilder _instance = new ControllerBuilder();
    private Func<IControllerFactory> _factoryThunk = () => null;
    public ControllerBuilder()
                : this(null)
    {
    }
    
    internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
    {
       _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
                                                          () => _factoryThunk(),
                                                          new DefaultControllerFactory { ControllerBuilder = this },
                                                          "ControllerBuilder.GetControllerFactory");
    }
    public IControllerFactory GetControllerFactory()
    {
      return _serviceResolver.Current;
    }

    此处只截取部分关键代码,查看“IResolver<T>”泛型接口,只有一个属性“T Current { get; }”,也就是返回当前T对象,那么对于方法“GetControllerFactory()”应该很快会领会到它是用来返回当前实现接口“IControllerFactory”的实例对象“DefaultControllerFactory ”,也就是说“DefaultControllerFactory ”是通过这种松散耦合的方式创建的。

      在MvcHandler中获取到了“factory”对象之后,紧接着就是通过当前上下文对象,控制器名称创建控制器的实例

    controller = factory.CreateController(RequestContext, controllerName);

    我们调试代码进入“DefaultControllerFactory ”的“CreateController”方法里面,如下

    public virtual IController CreateController(RequestContext requestContext, string controllerName)
    {
      if (requestContext == null)
      {
          throw new ArgumentNullException("requestContext");
      }
      if (String.IsNullOrEmpty(controllerName))
      {
          throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
      }
      Type controllerType = GetControllerType(requestContext, controllerName);
      IController controller = GetControllerInstance(requestContext, controllerType);
      return controller;
    }

    这段代码里面,先是获取当前控制器名称对应的类型,然后通过控制器类型获取控制器实例对象。代码定位到方法“GetControllerType”里面,如下

    protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
            {
                if (String.IsNullOrEmpty(controllerName))
                {
                    throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
                }
    
                // first search in the current route's namespace collection
                object routeNamespacesObj;
                Type match;
                if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj))
                {
                    IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
                    if (routeNamespaces != null && routeNamespaces.Any())
                    {
                        HashSet<string> namespaceHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
                        match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaceHash);
    
                        // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
                        if (match != null || false.Equals(requestContext.RouteData.DataTokens["UseNamespaceFallback"]))
                        {
                            // got a match or the route requested we stop looking
                            return match;
                        }
                    }
                }
    
                // then search in the application's default namespace collection
                if (ControllerBuilder.DefaultNamespaces.Count > 0)
                {
                    HashSet<string> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
                    match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaceDefaults);
                    if (match != null)
                    {
                        return match;
                    }
                }
    
                // if all else fails, search every namespace
                return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);
            }

    在这段代码里面,我们只重点关注通过名称空间匹配控制器类型,也是我们经常会遇到的错误,如图

    这个在上一篇介绍路由的时候已经提到了。

      当已经获取到了控制器类型之后,程序就通过该类型获取控制的实例对象,此时调试代码进入方法“GetControllerInstance”,如下

    protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
            {
                if (controllerType == null)
                {
                    throw new HttpException(404,
                                            String.Format(
                                                CultureInfo.CurrentCulture,
                                                MvcResources.DefaultControllerFactory_NoControllerFound,
                                                requestContext.HttpContext.Request.Path));
                }
                if (!typeof(IController).IsAssignableFrom(controllerType))
                {
                    throw new ArgumentException(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,
                            controllerType),
                        "controllerType");
                }
                return ControllerActivator.Create(requestContext, controllerType);
            }

    此方法很简单,先是判断控制器类型是否为空对象,然后判断当前的控制器实例是否可以从指定 “IController”的实例分配,当两个条件都过了,此时通过内部的私有类“DefaultControllerActivator”来创建控制器实例了,如下

    public IController Create(RequestContext requestContext, Type controllerType)
    {
      try
      {
          return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
      }
      catch (Exception ex)
      {
          throw new InvalidOperationException(
                            String.Format(
                                CultureInfo.CurrentCulture,
                                MvcResources.DefaultControllerFactory_ErrorCreatingController,
                                controllerType),
                            ex);
      }
    }

    各位看官,看清楚了吧,控制器实例然来是这样被创建并激活的。

      

      

  • 相关阅读:
    C# Linq 类似Scala中的map的函数
    Spark DataFrame NOT IN实现方法
    Scala scopt 命令行解析
    WPF 绑定到静态属性,可通知
    WPF GroupBox Header居中
    WPF开源项目整理(排名不分先后)
    Windows 上配置 Go 的 gRPC 编译环境
    C++20新线程 jthread 体验代码
    查找被删除但仍然占据磁盘的文件
    以Docker方式安装Redis集群
  • 原文地址:https://www.cnblogs.com/wucj/p/3113880.html
Copyright © 2011-2022 走看看