Asp.Net MVC 请求原理分析

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

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

Asp.Net Routing属于全局性的,所以它配置在全局Web.Config 中,我们可以在如下路径中找到:“C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\Web.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.

posted @ 2017-06-27 10:11  二奎  阅读(935)  评论(0编辑  收藏  举报