• Asp.Net MVC 请求原理分析


    分析Asp.Net MVC的请求过程,我们从以下几方面看:

    配置:IIS网站的配置可以分为两个块:全局 Web.Config 和本站 Web.Config 。

    Asp.Net Routing属于全局性的,所以它配置在全局Web.Config 中,我们可以在如下路径中找到:“C:WindowsMicrosoft.NETFramework64v4.0.30319ConfigWeb.config“  

      <?xml version="1.0" encoding="utf-8"?>
      <!-- the root web configuration file -->
      <configuration>
           <system.web>
               <httpModules>
                   <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
               </httpModules>
           </system.web>
       </configuration>


    其实我们仔细看下它的Init方法和PostResolveRequestCache,可以得到这个UrlRoutingModule是做什么的,我们看下代码:它继承自,IHttpModule ,到此我们就明白了Asp.Net MVC处理请求的HttpModule就是UrlRoutingModule。

    public class UrlRoutingModule : IHttpModule {     
            protected virtual void Init(HttpApplication application) {
              
                application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
            } 
     
            private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) {
                HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context); 
                PostResolveRequestCache(context);
            }      
            public virtual void PostResolveRequestCache(HttpContextBase context) { 
                // Match the incoming URL against the route table
                RouteData routeData = RouteCollection.GetRouteData(context);
                // Do nothing if no route found 
                if (routeData == null) {
                    return; 
                } 
                // If a route was found, get an IHttpHandler from the route's RouteHandler 
                IRouteHandler routeHandler = routeData.RouteHandler;           
                RequestContext requestContext = new RequestContext(context, routeData);
                // Remap IIS7 to our handler
                context.RemapHandler(httpHandler); 
            }
        }


    系统启动

    1. Asp.Net MVC 入口:Application_Start() ,调用 RouteConfig.RegisterRoutes(RouteTable.Routes);
        public class MvcApplication : System.Web.HttpApplication
          {
              protected void Application_Start()
              {
                  AreaRegistration.RegisterAllAreas();
                  FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                  RouteConfig.RegisterRoutes(RouteTable.Routes);
              }
          }
      public class RouteConfig
          {
              public static void RegisterRoutes(RouteCollection routes)
              {
                  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
              
                  routes.MapRoute(
                      name: "Default",
                      url: "{controller}/{action}",
                      defaults: new { controller = "Home", action = "Index" },
                      namespaces: new string[] { "ABC.Mr.Web.Controllers" }
                  );
              }
          }

    2. RouteConfig.RegisterRoutes进行路由配置:根据其中代码会创建一个RouteTable(路由表)实现URL到处理程序之间的映射。

    3. MapRoute会默认配置了一个MvcRouteHandler。

    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
            {
                if (routes == null)
                {
                    throw new ArgumentNullException("routes");
                }
                if (url == null)
                {
                    throw new ArgumentNullException("url");
                }
                Route route = new Route(url, new MvcRouteHandler())
                {
                    Defaults = CreateRouteValueDictionaryUncached(defaults),
                    Constraints = CreateRouteValueDictionaryUncached(constraints),
                    DataTokens = new RouteValueDictionary()
                };
                ConstraintValidation.Validate(route);
                if ((namespaces != null) && (namespaces.Length > 0))
                {
                    route.DataTokens[RouteDataTokenKeys.Namespaces] = namespaces;
                }
                routes.Add(name, route);
                return route;
            }
    public class MvcRouteHandler : IRouteHandler
        {
            private IControllerFactory _controllerFactory;
            public MvcRouteHandler()
            {
            }
            public MvcRouteHandler(IControllerFactory controllerFactory)
            {
                _controllerFactory = controllerFactory;
            }
            protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
                return new MvcHandler(requestContext);
            }
            protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
            {
                string controllerName = (string)requestContext.RouteData.Values["controller"];
                if (String.IsNullOrWhiteSpace(controllerName))
                {
                    throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
                }
                IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
                return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
            }
            #region IRouteHandler Members
            IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
            {
                return GetHttpHandler(requestContext);
            }
            #endregion
        }

    4. MvcHandler实现了三个接口IHttpAsyncHandler, IHttpHandler, IRequiresSessionState4.MvcRouteHandler继承IRouteHandler,并实现了GetHttpHandler接口,返回一个IHttpHandler :MvcHandler

    public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
        {
            private static readonly object _processRequestTag = new object();
            internal static readonly string MvcVersion = GetMvcVersionString();
            public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
            private ControllerBuilder _controllerBuilder;
            public MvcHandler(RequestContext requestContext)
            {
                if (requestContext == null)
                {
                    throw new ArgumentNullException("requestContext");
                }
                RequestContext = requestContext;
            }
            internal ControllerBuilder ControllerBuilder
            {
                get
                {
                    if (_controllerBuilder == null)
                    {
                        _controllerBuilder = ControllerBuilder.Current;
                    }
                    return _controllerBuilder;
                }
                set { _controllerBuilder = value; }
            }
            public static bool DisableMvcResponseHeader { get; set; }
            protected virtual bool IsReusable
            {
                get { return false; }
            }
            public RequestContext RequestContext { get; private set; }
            protected internal virtual void AddVersionHeader(HttpContextBase httpContext)
            {
                if (!DisableMvcResponseHeader)
                {
                    httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);
                }
            }
            protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
            {
                HttpContextBase httpContextBase = new HttpContextWrapper(httpContext);
                return BeginProcessRequest(httpContextBase, callback, state);
            }
            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)
                {
                    // asynchronous controller
                    // Ensure delegates continue to use the C# Compiler static delegate caching optimization.
                    BeginInvokeDelegate<ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, ProcessRequestState innerState)
                    {
                        try
                        {
                            return innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);
                        }
                        catch
                        {
                            innerState.ReleaseController();
                            throw;
                        }
                    };
                    EndInvokeVoidDelegate<ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, ProcessRequestState innerState)
                    {
                        try
                        {
                            innerState.AsyncController.EndExecute(asyncResult);
                        }
                        finally
                        {
                            innerState.ReleaseController();
                        }
                    };
                    ProcessRequestState outerState = new ProcessRequestState() 
                    {
                        AsyncController = asyncController, Factory = factory, RequestContext = RequestContext
                    };
                    
                    SynchronizationContext callbackSyncContext = SynchronizationContextUtil.GetSynchronizationContext();
                    return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, outerState, _processRequestTag, callbackSyncContext: callbackSyncContext);
                }
                else
                {
                    // synchronous controller
                    Action action = delegate
                    {
                        try
                        {
                            controller.Execute(RequestContext);
                        }
                        finally
                        {
                            factory.ReleaseController(controller);
                        }
                    };
                    return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
                }
            }
            protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)
            {
                AsyncResultWrapper.End(asyncResult, _processRequestTag);
            }
            private static string GetMvcVersionString()
            {
                // DevDiv 216459:
                // This code originally used Assembly.GetName(), but that requires FileIOPermission, which isn't granted in
                // medium trust. However, Assembly.FullName *is* accessible in medium trust.
                return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
            }
            protected virtual void ProcessRequest(HttpContext httpContext)
            {
                HttpContextBase httpContextBase = new HttpContextWrapper(httpContext);
                ProcessRequest(httpContextBase);
            }
            protected internal virtual void ProcessRequest(HttpContextBase httpContext)
            {
                IController controller;
                IControllerFactory factory;
                ProcessRequestInit(httpContext, out controller, out factory);
                try
                {
                    controller.Execute(RequestContext);
                }
                finally
                {
                    factory.ReleaseController(controller);
                }
            }
            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));
                }
            }
            private void RemoveOptionalRoutingParameters()
            {
                RouteValueDictionary rvd = RequestContext.RouteData.Values;
                // Ensure delegate is stateless
                rvd.RemoveFromDictionary((entry) => entry.Value == UrlParameter.Optional);
            }
            #region IHttpHandler Members
            bool IHttpHandler.IsReusable
            {
                get { return IsReusable; }
            }
            void IHttpHandler.ProcessRequest(HttpContext httpContext)
            {
                ProcessRequest(httpContext);
            }
            #endregion
            #region IHttpAsyncHandler Members
            IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
            {
                return BeginProcessRequest(context, cb, extraData);
            }
            void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
            {
                EndProcessRequest(result);
            }
            #endregion
            // Keep as value type to avoid allocating
            private struct ProcessRequestState
            {
                internal IAsyncController AsyncController;
                internal IControllerFactory Factory;
                internal RequestContext RequestContext;
                internal void ReleaseController()
                {
                    Factory.ReleaseController(AsyncController);
                }
            }
        }


    请求开始

    1. 在IIS中 HTTP.SYS文件负责把请求传入相应的应用程序池中。(当创建一个应用程序池,该池的ID就会生成并在HTTP.SYS文件中注册)

    2. 当应用程序池接收到请求,会接着传给工作进程w3wp.exe,该进程检查来请求的URL后缀以确定加载哪个ISAPI扩展。ASP.NET加载时会附带自己的ISAPI扩展(aspnet_isapi.dll),以便在IIS中映射。

        注意:如果先安装了asp.net,然后再安装IIS,就需要通过aspnet_regiis命令来注册ASP.NET中的ISAPI扩展。

    3. 当工作进程加载了aspnet_isapi.dll, 就会构造一个HttpRuntime类,该类是应用程序的入口,通过ProcessRequest方法处理请求。

    4. 当这个方法被调用,一个HttpContext的实例就产生了。可通过HTTPContent.Current获取到这个实例,且该实例会在整个生命周期中存活,我们通过它可以获取到一些常用对象,如Request,Response,Session 等

    5. HttpRuntime会通过HttpApplicationFactory类加载一个HttpApplication对象。每一次请求都要穿过UrlRoutingModule到达HttpHandler,以便被响应.

    6. 上面已经讲到了UrlRoutingModule,当PostResolveRequestCache事件得到响应后,首先就要在路由中进匹配。

        public virtual void PostResolveRequestCache(HttpContextBase context) { 
                // Match the incoming URL against the route table
                RouteData routeData = RouteCollection.GetRouteData(context);
                // Do nothing if no route found 
                if (routeData == null) {
                    return; 
                } 
                // If a route was found, get an IHttpHandler from the route's RouteHandler 
                IRouteHandler routeHandler = routeData.RouteHandler;           
                RequestContext requestContext = new RequestContext(context, routeData);
                // Remap IIS7 to our handler
                context.RemapHandler(httpHandler); 
            }

    7. UrlRoutingModule在RouteCollection中查找Request匹配的RouteHandler,默认是MvcRouteHandler。

    8. MvcRouteHandler 创建 MvcHandler实例. 这就是我们得到的routeHandler,其实就是 MvcHandler

    9. MvcHandler执行 ProcessRequest.

    protected internal virtual void ProcessRequest(HttpContextBase httpContext)
            {
                IController controller;
                IControllerFactory factory;
                ProcessRequestInit(httpContext, out controller, out factory);
                try
                {
                    controller.Execute(RequestContext);
                }
                finally
                {
                    factory.ReleaseController(controller);
                }
            }

    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)); } }

    10. MvcHandler 使用 IControllerFactory 获得实现了IController接口的实例,找到对应的业务controller

    11. 根据Request触发业务controller的和Action方法   获取Controller与Action的描述信息和过滤器信息

    public abstract class ControllerBase : IController
        {
            protected virtual void Execute(RequestContext requestContext)
            {
                if (requestContext == null)
                {
                    throw new ArgumentNullException("requestContext");
                }
                if (requestContext.HttpContext == null)
                {
                    throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
                }
    
                VerifyExecuteCalledOnce();
                Initialize(requestContext);
    
                using (ScopeStorage.CreateTransientScope())
                {
                    ExecuteCore();
                }
            }
         // 抽象方法-让Controller去具体实现
            protected abstract void ExecuteCore();
        }
    public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
        {
            protected override void ExecuteCore()
            {
                PossiblyLoadTempData();
                try
                {
                    string actionName = GetActionName(RouteData);
                    if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
                    {
                        HandleUnknownAction(actionName);
                    }
                }
                finally
                {
                    PossiblySaveTempData();
                }
            }
        }
    private static string GetActionName(RouteData routeData)
            {
                Contract.Assert(routeData != null);
    
                // If this is an attribute routing match then the 'RouteData' has a list of sub-matches rather than
                // the traditional controller and action values. When the match is an attribute routing match
                // we'll pass null to the action selector, and let it choose a sub-match to use.
                if (routeData.HasDirectRouteMatch())
                {
                    return null;
                }
                else
                {
                    return routeData.GetRequiredString("action");
                }
            }
    public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
            {
                if (controllerContext == null)
                {
                    throw new ArgumentNullException("controllerContext");
                }
    
                Contract.Assert(controllerContext.RouteData != null);
                if (String.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch())
                {
                    throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
                }
    
                ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
                ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
    
                if (actionDescriptor != null)
                {
                    FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
    
                    try
                    {
                        AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);
    
                        if (authenticationContext.Result != null)
                        {
                            // An authentication filter signaled that we should short-circuit the request. Let all
                            // authentication filters contribute to an action result (to combine authentication
                            // challenges). Then, run this action result.
                            AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
                                controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
                                authenticationContext.Result);
                            InvokeActionResult(controllerContext, challengeContext.Result ?? authenticationContext.Result);
                        }
                        else
                        {
                            AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
                            if (authorizationContext.Result != null)
                            {
                                // An authorization filter signaled that we should short-circuit the request. Let all
                                // authentication filters contribute to an action result (to combine authentication
                                // challenges). Then, run this action result.
                                AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
                                    controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
                                    authorizationContext.Result);
                                InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
                            }
                            else
                            {
                                if (controllerContext.Controller.ValidateRequest)
                                {
                                    ValidateRequest(controllerContext);
                                }
    
                                IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
                                ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
    
                                // The action succeeded. Let all authentication filters contribute to an action result (to
                                // combine authentication challenges; some authentication filters need to do negotiation
                                // even on a successful result). Then, run this action result.
                                AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
                                    controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
                                    postActionContext.Result);
                                InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters,
                                    challengeContext.Result ?? postActionContext.Result);
                            }
                        }
                    }
                    catch (ThreadAbortException)
                    {
                        // This type of exception occurs as a result of Response.Redirect(), but we special-case so that
                        // the filters don't see this as an error.
                        throw;
                    }
                    catch (Exception ex)
                    {
                        // something blew up, so execute the exception filters
                        ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
                        if (!exceptionContext.ExceptionHandled)
                        {
                            throw;
                        }
                        InvokeActionResult(controllerContext, exceptionContext.Result);
                    }
    
                    return true;
                }
    
                // notify controller that no method matched
                return false;
            }

     12. 上面的代码显示,执行action之前 ,会对controller和action的filter做判断 ,并且进行认证和授权的判断。

    13,验证全部通过后会通过GetParameterValues获取方法的参数信息,执行方法InvokeActionResultWithFilters:

            protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
            {
                ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
                Func<ActionExecutedContext> continuation = () =>
                                                           new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */)
                                                           {
                                                               Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
                                                           };
    
                // need to reverse the filter list because the continuations are built up backward
                Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
                                                                                (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
                return thunk();
            }
            protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
            {
                object returnValue = actionDescriptor.Execute(controllerContext, parameters);
                ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);
                return result;
            }
            protected virtual ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
            {
                if (actionReturnValue == null)
                {
                    return new EmptyResult();
                }
    
                ActionResult actionResult = (actionReturnValue as ActionResult) ??
                                            new ContentResult { Content = Convert.ToString(actionReturnValue, CultureInfo.InvariantCulture) };
                return actionResult;
            }

    14 到此已经返回了我们熟悉的ActoinResult.

  • 相关阅读:
    CentOS7系统基本操作
    python3安装
    nodejs基础【持续更新中】
    基于Jenkins实现持续集成【持续更新中】
    git之merge和rebase的区别
    服务器为什么这么慢?耗尽了CPU、RAM和磁盘I/O资源
    编程的四个境界
    Gunicorn独角兽
    Python 中 logging 日志模块在多进程环境下的使用
    vue+webpack怎么分环境进行打包
  • 原文地址:https://www.cnblogs.com/hankuikui/p/7068833.html
Copyright © 2020-2023  润新知