ASP.NET和ASP.NET MVC的HttpApplication请求处理管道有共同的部分和不同之处,本系列将体验ASP.NET MVC请求处理管道生命周期的19个关键环节。
①以IIS6.0为例,首先由w3wp.exe维护着一个工作进程
②如果是第一次加载,由Aspnet_isapi.dll加载.NET运行时
③一个工作进程里有一个应用程序池,其中可以承载多个应用程序域AppDomain
④HTTP.SYS接收请求,通过应用程序域工厂AppDomainFactory创建应用程序域AppDomain
⑤一个IsapiRuntime被加载,并创建一个IsapiWorkerRequest对象封装当前的HTTP请求,并把该IsapiWorkerRequest对象传递给ASP.NET的HttpRuntime运行时,此时, HTTP请求开始进入ASP.NET请求管道
也就是说,HttpRuntime是ASP.NET请求管道的入口。当请求进来,首先进入HttpRuntime,由HttpRuntime来决定如何处理请求。默认情况下,在machine.config和Web.config中并没有显式定义httpRuntime节点,但该节点是有默认值的,如下:
通常,我们可以在Web.config中更改httpRuntime节点的默认值,如下:
⑥HttpRuntime维护着一个HttpApplication池,当有HTTP请求过来,从池中选取可用的HttpApplication处理请求
HttpApplication有19个管道事件,分别是:
1、BeginRequest:HTTP管道开始处理请求时,会触发BeginRequest事件 2、AuthenticateRequest:安全模块对请求进行身份验证时触发该事件 3、PostAuthenticateRequest:安全模块对请求进行身份验证后触发该事件 4、AuthorizeRequest:安全模块对请求进程授权时触发该事件 5、PostAuthorizeRequest:安全模块对请求进程授权后触发该事件 6、ResolveRequestCache:缓存模块利用缓存直接对请求进程响应时触发该事件 7、PostResolveRequestCache:缓存模块利用缓存直接对请求进程响应后触发该事件 8、PostMapRequestHandler:对于访问不同的资源类型,ASP.NET具有不同的HttpHandler对其进程处理。对于每个请求,ASP.NET会根据扩展名选择匹配相应的HttpHandler类型,成功匹配后触发该事件 9、AcquireRequestState:状态管理模块获取基于当前请求相应的状态(比如SessionState)时触发该事件 10、PostAcquireRequestState:状态管理模块获取基于当前请求相应的状态(比如SessionState)后触发该事件 11、PreRequestHandlerExecute:在实行HttpHandler前触发该事件 12、PostRequestHandlerExecute:在实行HttpHandler后触发该事件 13、ReleaseRequestState:状态管理模块释放基于当前请求相应的状态时触发该事件 14、PostReleaseRequestState:状态管理模块释放基于当前请求相应的状态后触发该事件 15、UpdateRequestCache:缓存模块将HttpHandler处理请求得到的相应保存到输出缓存时触发该事件 16、PostUpdateRequestCache:缓存模块将HttpHandler处理请求得到的相应保存到输出缓存后触发该事件 17、LogRequest:为当前请求进程日志记录时触发该事件 18、PostLogReques:为当前请求进程日志记录后触发该事件 19、EndRequest:整个请求处理完成后触发该事件
我们可以在全局配置文件Global.asax中,按照约定的规则Application_{Event Name}来对管道事件定制:
⑦根据IsapiWorkerRequest对象,HttpRuntime创建HttpContext对象
⑧HttpApplicationFactory创建新的或者从HttpApplication池获取现有的、可用的HttpApplication对象
HttpApplication的工作包括:
● 初始化的时候加载全部的HttpModule
● 接收请求
● 在不同阶段引发不同的事件,使得HttpModule通过订阅事件的方式加入到请求的处理过程中
● 在一个特定阶段获取一个IHttpHandler实例,最终将请求交给具体的IHttpHandler来实现
⑨接下来,就是HttpModules发挥作用的时候
所有的HttpModules都实现了IHttpModule接口:
public interface IHttpModule { void Init(HttpApplication app); void Dispose(); }
可见,HttoModules正是由Init方法,根据传入的HttpApplication类型参数,订阅了HttpApplication的所有事件。
我们自定义一个HttpModule:
public class TestModule : IHttpModule { public void Dispose(){} public void Init(HttpApplication app) { app.PostAcquireRequestState += new EventHandler(app_PostAcuiredRequestState); app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute); } void app_PreRequestHandlerExecute(object sender, EventArgs e) { //TODO: } void app_PostAcquiredRequestState(object sender, EventArgs e) { //TODO: } }
⑩当某个请求与一个规则匹配后,ASP.NET会调用匹配的HttpHandlerFactory的GetHandler方法来获取一个HttpHandler实例, 最后由一个HttpHandler实例来处理当前请求,生成响应内容
所有的HttpHandlers都实现了IHttpHandler接口:
public interface IHttpHandler { bool IsReusable{get;} void ProcessRequest(HttpContext context); }
比如我们可以自定义一个HttpHandler来响应一类特定的请求:
public class Login : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string username = context.Request.Form["name"]; string password = context.Request.Form["password"]; if(password="sth") { System.Web.Security.FormsAuthentication.SetAuthCookie(username, false); context.Response.Write("ok"); } else { context.Response.Write("用户名和密码不正确"); } } }
⑾ASP.NET MVC的入口在UrlRoutingModule,即订阅了HttpApplication的第7个管道事件PostResolveRequestCahce,换句话说,是在HtttpApplication的第7个管道事件处对请求进行了拦截
UrlRouteModlue实现了IHttpModule:
public class UrlRoutingModule : IHttpModule { // Fields private static readonly object _contextKey = new object(); private static readonly object _requestDataKey = new object(); private RouteCollection _routeCollection; // Methods protected virtual void Dispose() { } protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] == null) { application.Context.Items[_contextKey] = _contextKey; application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); } } private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context); this.PostResolveRequestCache(context); } [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")] public virtual void PostMapRequestHandler(HttpContextBase context) { } public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = this.RouteCollection.GetRouteData(context); if (routeData != null) { IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); if (httpHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() })); } if (httpHandler is UrlAuthFailureHandler) { if (!FormsAuthenticationModule.FormsAuthRequired) { throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3")); } UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this); } else { context.RemapHandler(httpHandler); } } } } void IHttpModule.Dispose() { this.Dispose(); } void IHttpModule.Init(HttpApplication application) { this.Init(application); } // Properties public RouteCollection RouteCollection { get { if (this._routeCollection == null) { this._routeCollection = RouteTable.Routes; } return this._routeCollection; } set { this._routeCollection = value; } } }
UrlRoutingModule是在Web.config或默认的web.config中配置:
......
⑿而在请求到达UrlRoutingModule之前,我们在全局文件中做了如下配置
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { ...... BundleConfig.RegisterBundles(BundleTable.Bundles); } } public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
这意味着:在HttpApplication的第一个管道事件BeginRequest处,通过MapRoute()方法把路由注册到了RouteCollection中。在实际使用中,UrlRoutingModule是通过RouteTable的静态属性RouteCollection获取路由。
⒀当请求到达UrlRoutingModule的时候,UrlRoutingModule取出请求中的Controller、Action等RouteData信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler,即MVCRouteHandler
MVCRouteHandler是用来生成实现IHttpHandler接口的MvcHandler:
namespace System.Web.Routing { public interface IRouteHandler { IHttpHandler GetHttpHandler(RequestContext requestContext); } }
UrlRoutingModule如何把请求交给MVCRouteHandler?
通过分析UrlRoutingModule的源码可以看到:
//通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例
RouteData routeData = this.RouteCollection.GetRouteData(context);
//再从RouteData中获取MVCRouteHandler
IRouteHandler routeHandler = routeData.RouteHandler;
为什么可以从RouteData中拿到MVCRouteHadnler呢?
因为当我们在HttpApplication的第一个管道事件,使用MapRoute()方法注册路由的时候,已经通过Route类的构造函数把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 = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; }
⒁MVCRouteHandler把请求交给MvcHandler
还是从UrlRoutingModule的源码可以看到,通过HttpHandler的GetHttpHandler()方法获取到了实现了IHttpHandler接口的MVCHandler:
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); context.RemapHandler(httpHandler);
MvcHandler的部分源码为:
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState { protected internal virtual void ProcessRequest(HttpContextBase httpContext) { SecurityUtil.ProcessInApplicationTrust(() => { IController controller; IControllerFactory factory; ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory try { controller.Execute(RequestContext); } finally { factory.ReleaseController(controller); } }); } private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) { bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current); if (isRequestValidationEnabled == true) { ValidationUtility.EnableDynamicValidation(HttpContext.Current); } AddVersionHeader(httpContext); RemoveOptionalRoutingParameters(); string controllerName = RequestContext.RouteData.GetRequiredString("controller"); factory = ControllerBuilder.GetControllerFactory(); controller = factory.CreateController(RequestContext, controllerName); if (controller == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName)); } } }
⒂从以上可以看出:首先通过ControllerBuilder的静态方法GetControllerFactory获取到实现IControllerFactory接口的ControllerFactory,然后根据从上下文中的路由数据中拿到controller名称,并据此创建实现IController接口的Controller
Controller派生于ControllerBase, 而ControllerBase实现了IController接口。ControllerBase的部分源码如下:
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(); } } protected abstract void ExecuteCore(); ...... }
从中可以看成:
● 每次调用controller,都会执行基类ControllerBase的Execute()方法
● Execute()方法又会调用ExecuteCore()这个抽象方法
● ExecuteCore()这个抽象方法的实现被定义在Controller中
● 在Controller中的ExecuteCore()方法会调用ActionInvoker的InvokeAction()方法
⒃ActionInvoker激发Action方法
ActionInvoker实现了IActionInvoker接口:
public interface IActionInvoker { bool InvokeAction(ControllerContext controllerContext, string actionName); }
MVC默认的ActionInvoker是ControllerActionInvoker。
在Controller类中,提供了类型为IActionInvoker的属性ActionInvoker,当执行ExecuteCore()方法时会让这个ActionInvoker调用InvokeAction()方法激发Action。如下:
public class Controller { ...... private IActionInvoker _actionInvoker; public IActionInvoker ActionInvoker { get { if(_actionInvoker == null) { _actionInvoker = CreateActionInvoker(); } return _actionInvoker; } set { _actionInvoker = value; } } protected virtual IActionInvoker CreateActionInvoker() { return new ControllerActionInvoker(); } public override void ExecuteCore() { ActionInvoker.InvokeAction(...); } ..... }
ActionInvoker在执行InvokeAction()方法时会需要有关Controller和Action的相关信息,实际上,Controller信息(比如Controller的名称、类型、包含的Action等)被封装在ControllerDescriptor这个类中,Action信息(比如Action的名称、参数、属性、过滤器等)被封装在ActionDescriptor中。
另外,ActionDescriptor还提供了一个FindAction()方法,用来找到需要被执行的Action。
⒄ActionInvoker在执行InvokeAction()方法返回ActionResult
ActionResult是一个抽象类:
public abstract class ActionResult { public abstract void ExecuteResult(ControllerContext context); }
如果ActionResult是非ViewResult,比如JsonResult, ContentResult,这些内容将直接被输送到Response响应流中,显示给客户端;如果是ViewResult,就会进入下一个渲染视图环节。
⒅ViewEngine找到需要被渲染的视图
默认的有Razor View Engine和Web Form View Engine,实现IViewEngine接口。
IViewEngine接口方法:
● FindPartialView
● FindView
● ReleaseView
如果要创建自定义View Engine,只需要派生于VirtualPathProviderViewEngine这个类。
⒆View被加载成WebViewPage
调用ViewResult的ExecuteResult()方法,通过IView的Render()方法渲染成Html。
public abstract class ViewResultBase : ActionResult { public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (String.IsNullOrEmpty(ViewName)) { ViewName = context.RouteData.GetRequiredString("action"); } ViewEngineResult result = null; if (View == null) { //通过视图引擎获取到ViewEngineResult ,此时模板页面【aspx】被加载成了WebViewPageresult = FindView(context); View = result.View; } TextWriter writer = context.HttpContext.Response.Output; ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer); View.Render(viewContext, writer); if (result != null) { result.ViewEngine.ReleaseView(context, View); } } }
void Application_Start(object sender, EventArgs e) {} void Application_End(object sender, EventArgs e) {} void Application_Error(object sender, EventArgs e) {} void Session_Start(object sender, EventArgs e) {} void Session_End(object sender, EventArgs e) {} ......