白话ASP.NET MVC之二:Controller激活系统的概览

前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了。我们注册了路由对象Route,UrlRoutingModule截获请求,把当前请求的Url地址和RouteTable路由表中注册的路由对象一个一个的比较,如果没有找到就返回Null,请求就到此终止了。如果有匹配的路由对象,选择第一个匹配的Route对象,并且根据该Route对象,生成了路由数据RouteData对象,本对象是为了封装成RequestContext对象,RequestContext对象封装了RouteData和HttpContext对象。

其实RequestContext对象也是作为参数使用的,我们根据Route对象获得了RouteData,该对象有一个RouteHandler属性,这个属性的值在ASP.NET MVC中是MvcRouteHandler。RouteHandler主要的作用是提供用于处理最终请求的HttpHandler对象,代码如下:

IHttpHandler=RouteData.RouteHandler.GetHttpHandler(requestContext);
HttpContext.Remap(IHttpHandler);

上文中我们获取到的RequestContext,将此作为参数调用RouteHandler的GetHttpHandler方法,我们获得了IHttpHandler对象,我们需要把请求交给这个HttpHandler来处理,通过HttpContext.Remap(Handler)实现请求的交接,这个Handler在ASP.NET MVC中就是MvcHandler,我们既然获得了HttpHandler对象,也实现了请求的交接,下一步该做什么呢?

一、概览

我们获得了用于处理请求HttpHandler对象,下一步最重要的任务是把Controller对象找到,RouteData对象的Values属性的Controller键和Action键的值只是一个字符串的结果,我们要通过这个名称找到具体的Controller类型实例,才能执行Action方法,给客户想要的应答。这篇文章就是Controller的激活方法的详解。

ASP.NET MVC系统真的很庞大,也很复杂,如果我们一上来就瀑布式的说,就像流水账一样,估计也不太容易说明白。为了便于理解,我们人为的把ASP.NET MVC这么庞大的系统拆分成相互独立,又有一定联系的多个子系统,然后我们各个击破,理解起来也就简单了,最后在把各个部分整合在一起,理解就全面了。今天我们要说的这一部分暂时叫做Controller激活系统吧,激活系统有两个含义,一是我们要找到我们需要的Controller对象,并实例化;二是我们药缓存他,并要执行它。

我们先来看看MvcHandler的源码吧,有助于我们的理解,代码如下:

  1         /// <summary>Selects the controller that will handle an HTTP request.</summary>
  2     public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
  3     {
  4         private struct ProcessRequestState
  5         {
  6             internal IAsyncController AsyncController;
  7
  8             internal IControllerFactory Factory;
  9
 10             internal RequestContext RequestContext;
 11
 12             internal void ReleaseController()
 13             {
 14                 this.Factory.ReleaseController(this.AsyncController);
 15             }
 16         }
 17
 18         private static readonly object _processRequestTag = new object();
 19
 20         internal static readonly string MvcVersion = MvcHandler.GetMvcVersionString();
 21
 22         /// <summary>Contains the header name of the ASP.NET MVC version.</summary>
 23         public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
 24
 25         private ControllerBuilder _controllerBuilder;
 26
 27         internal ControllerBuilder ControllerBuilder
 28         {
 29             get
 30             {
 31                 if (this._controllerBuilder == null)
 32                 {
 33                     this._controllerBuilder = ControllerBuilder.Current;
 34                 }
 35                 return this._controllerBuilder;
 36             }
 37             set
 38             {
 39                 this._controllerBuilder = value;
 40             }
 41         }
 42
 43         /// <summary>Gets or sets a value that indicates whether the MVC response header is disabled.</summary>
 44         /// <returns>true if the MVC response header is disabled; otherwise, false.</returns>
 45         public static bool DisableMvcResponseHeader
 46         {
 47             get;
 48             set;
 49         }
 50
 51         /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary>
 52         /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns>
 53         protected virtual bool IsReusable
 54         {
 55             get
 56             {
 57                 return false;
 58             }
 59         }
 60
 61         /// <summary>Gets the request context.</summary>
 62         /// <returns>The request context.</returns>
 63         public RequestContext RequestContext
 64         {
 65             get;
 66             private set;
 67         }
 68
 69         /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary>
 70         /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns>
 71         bool IHttpHandler.IsReusable
 72         {
 73             get
 74             {
 75                 return this.IsReusable;
 76             }
 77         }
 78
 79         /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcHandler" /> class.</summary>
 80         /// <param name="requestContext">The request context.</param>
 81         /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
 82         public MvcHandler(RequestContext requestContext)
 83         {
 84             if (requestContext == null)
 85             {
 86                 throw new ArgumentNullException("requestContext");
 87             }
 88             this.RequestContext = requestContext;
 89         }
 90
 91         /// <summary>Adds the version header by using the specified HTTP context.</summary>
 92         /// <param name="httpContext">The HTTP context.</param>
 93         protected internal virtual void AddVersionHeader(HttpContextBase httpContext)
 94         {
 95             if (!MvcHandler.DisableMvcResponseHeader)
 96             {
 97                 httpContext.Response.AppendHeader(MvcHandler.MvcVersionHeaderName, MvcHandler.MvcVersion);
 98             }
 99         }
100
101         /// <summary>Called by ASP.NET to begin asynchronous request processing.</summary>
102         /// <returns>The status of the asynchronous call.</returns>
103         /// <param name="httpContext">The HTTP context.</param>
104         /// <param name="callback">The asynchronous callback method.</param>
105         /// <param name="state">The state of the asynchronous object.</param>
106         protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
107         {
108             HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
109             return this.BeginProcessRequest(httpContext2, callback, state);
110         }
111
112         /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>
113         /// <returns>The status of the asynchronous call.</returns>
114         /// <param name="httpContext">The HTTP context.</param>
115         /// <param name="callback">The asynchronous callback method.</param>
116         /// <param name="state">The state of the asynchronous object.</param>
117         protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
118         {
119             IController controller;
120             IControllerFactory factory;
121             this.ProcessRequestInit(httpContext, out controller, out factory);
122             IAsyncController asyncController = controller as IAsyncController;
123             if (asyncController != null)
124             {
125                 BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)
126                 {
127                     IAsyncResult result;
128                     try
129                     {
130                         result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);
131                     }
132                     catch
133                     {
134                         innerState.ReleaseController();
135                         throw;
136                     }
137                     return result;
138                 };
139                 EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)
140                 {
141                     try
142                     {
143                         innerState.AsyncController.EndExecute(asyncResult);
144                     }
145                     finally
146                     {
147                         innerState.ReleaseController();
148                     }
149                 };
150                 MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState
151                 {
152                     AsyncController = asyncController,
153                     Factory = factory,
154                     RequestContext = this.RequestContext
155                 };
156                 SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();
157                 return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);
158             }
159             Action action = delegate
160             {
161                 try
162                 {
163                     controller.Execute(this.RequestContext);
164                 }
165                 finally
166                 {
167                     factory.ReleaseController(controller);
168                 }
169             };
170             return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);
171         }
172
173         /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>
174         /// <param name="asyncResult">The asynchronous result.</param>
175         protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)
176         {
177             AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag);
178         }
179
180         private static string GetMvcVersionString()
181         {
182             return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
183         }
184
185         /// <summary>Processes the request by using the specified HTTP request context.</summary>
186         /// <param name="httpContext">The HTTP context.</param>
187         protected virtual void ProcessRequest(HttpContext httpContext)
188         {
189             HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
190             this.ProcessRequest(httpContext2);
191         }
192
193         /// <summary>Processes the request by using the specified base HTTP request context.</summary>
194         /// <param name="httpContext">The HTTP context.</param>
195         protected internal virtual void ProcessRequest(HttpContextBase httpContext)
196         {
197             IController controller;
198             IControllerFactory controllerFactory;
199             this.ProcessRequestInit(httpContext, out controller, out controllerFactory);
200             try
201             {
202                 controller.Execute(this.RequestContext);
203             }
204             finally
205             {
206                 controllerFactory.ReleaseController(controller);
207             }
208         }
209
210         private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
211         {
212             HttpContext current = HttpContext.Current;
213             if (current != null && ValidationUtility.IsValidationEnabled(current) == true)
214             {
215                 ValidationUtility.EnableDynamicValidation(current);
216             }
217             this.AddVersionHeader(httpContext);
218             this.RemoveOptionalRoutingParameters();
219             string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
220             factory = this.ControllerBuilder.GetControllerFactory();
221             controller = factory.CreateController(this.RequestContext, requiredString);
222             if (controller == null)
223             {
224                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
225                 {
226                     factory.GetType(),
227                     requiredString
228                 }));
229             }
230         }
231
232         private void RemoveOptionalRoutingParameters()
233         {
234             RouteValueDictionary values = this.RequestContext.RouteData.Values;
235             values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional);
236         }
237
238         /// <summary>Enables processing of HTTP Web requests by a custom HTTP handler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.</summary>
239         /// <param name="httpContext">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) that are used to service HTTP requests.</param>
240         void IHttpHandler.ProcessRequest(HttpContext httpContext)
241         {
242             this.ProcessRequest(httpContext);
243         }
244
245         /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>
246         /// <returns>The status of the asynchronous call.</returns>
247         /// <param name="context">The HTTP context.</param>
248         /// <param name="cb">The asynchronous callback method.</param>
249         /// <param name="extraData">The data.</param>
250         IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
251         {
252             return this.BeginProcessRequest(context, cb, extraData);
253         }
254
255         /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>
256         /// <param name="result">The asynchronous result.</param>
257         void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
258         {
259             this.EndProcessRequest(result);
260         }
261     }

MvcHandler里面这个两个方法定义了Controller激活系统实现的骨架:

 1         /// <summary>Processes the request by using the specified base HTTP request context.</summary>
 2         /// <param name="httpContext">The HTTP context.</param>
 3         protected internal virtual void ProcessRequest(HttpContextBase httpContext)
 4         {
 5             IController controller;
 6             IControllerFactory controllerFactory;
 7             this.ProcessRequestInit(httpContext, out controller, out controllerFactory);
 8             try
 9             {
10                 controller.Execute(this.RequestContext);
11             }
12             finally
13             {
14                 controllerFactory.ReleaseController(controller);
15             }
16         }
17
18         private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
19         {
20             HttpContext current = HttpContext.Current;
21             if (current != null && ValidationUtility.IsValidationEnabled(current) == true)
22             {
23                 ValidationUtility.EnableDynamicValidation(current);
24             }
25             this.AddVersionHeader(httpContext);
26             this.RemoveOptionalRoutingParameters();
27             string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
28             factory = this.ControllerBuilder.GetControllerFactory();
29             controller = factory.CreateController(this.RequestContext, requiredString);
30             if (controller == null)
31             {
32                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
33                 {
34                     factory.GetType(),
35                     requiredString
36                 }));
37             }
38         }

代码不是很难,大家一看就能看懂,下一节好好的介绍一些Controller激活系统中所涉及到的各个对象,具体的解析规则要到下一篇文章了,否则文章就太长了。

二、Controller对象激活详述

ASP.NET MVC中,我们人为分解的当前系统叫:Controller激活系统,其实这个激活系统里面包含的对象也不是很多,主要有我们要实例化的Controller对象,管理Controller的ControllerFactory对象,管理ControllerFactory的ControllerBuilder对象,这三个主要对象构成了我们的激活系统。

1、我们先看看Controller类型的定义吧

我们所说的Controller其实是指实现了IController接口的某个类型实例。Controller是一个可以执行的对象,它的执行体现在对Execute方法上的调用。一说到执行我们是不是就会想到同步执行和异步执行呢,既然Controller是一个可以执行的对象,他会不会也具有同步和异步的执行呢,答案是肯定的,我们先来看看两个接口吧:

public interface IController
{
    /// <summary>Executes the specified request context.</summary>
    /// <param name="requestContext">The request context.</param>
    void Execute(RequestContext requestContext);
}

这个就是同步版的接口,该接口的命名空间是:System.Web.Mvc,当目标Controller对象被成功激活后,对请求的后续处理和最终响应都通过执行者Execute方法来实现。我们再看看异步版的接口定义吧:

 1 public interface IAsyncController : IController
 2 {
 3     /// <summary>Executes the specified request context.</summary>
 4     /// <returns>The status of the asynchronous operation.</returns>
 5     /// <param name="requestContext">The request context.</param>
 6     /// <param name="callback">The asynchronous callback method.</param>
 7     /// <param name="state">The state.</param>
 8     IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);
 9
10     /// <summary>Ends the asynchronous operation.</summary>
11     /// <param name="asyncResult">The asynchronous result.</param>
12     void EndExecute(IAsyncResult asyncResult);
13 }

异步版接口定义的命名空间是:System.Web.Mvc.Async,我们看到IAsyncController是实现了IController的接口,Controller的异步执行是通过调用BeginExecute和EndExecute方法来实现的。我们通过Visual Studio创建的ASP.NET MVC项目,我们自定义的Controller实现的基类是Controller类型,不是直接实现以上接口的。我们看看Controller类型的定义吧:

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
{
//代码省略
}

Controller是抽象类型,实现了很多接口,因为该类的代码太多,所以省略了部分内部代码,这个里面牵扯到另外一个类,就是ControllerBase,

  1 /// <summary>Represents the base class for all MVC controllers.</summary>
  2 public abstract class ControllerBase : IController
  3 {
  4     private readonly SingleEntryGate _executeWasCalledGate = new SingleEntryGate();
  5
  6     private DynamicViewDataDictionary _dynamicViewDataDictionary;
  7
  8     private TempDataDictionary _tempDataDictionary;
  9
 10     private bool _validateRequest = true;
 11
 12     private IValueProvider _valueProvider;
 13
 14     private ViewDataDictionary _viewDataDictionary;
 15
 16     /// <summary>Gets or sets the controller context.</summary>
 17     /// <returns>The controller context.</returns>
 18     public ControllerContext ControllerContext
 19     {
 20         get;
 21         set;
 22     }
 23
 24     /// <summary>Gets or sets the dictionary for temporary data.</summary>
 25     /// <returns>The dictionary for temporary data.</returns>
 26     public TempDataDictionary TempData
 27     {
 28         get
 29         {
 30             if (this.ControllerContext != null && this.ControllerContext.IsChildAction)
 31             {
 32                 return this.ControllerContext.ParentActionViewContext.TempData;
 33             }
 34             if (this._tempDataDictionary == null)
 35             {
 36                 this._tempDataDictionary = new TempDataDictionary();
 37             }
 38             return this._tempDataDictionary;
 39             }
 40         set
 41         {
 42             this._tempDataDictionary = value;
 43         }
 44     }
 45
 46     /// <summary>Gets or sets a value that indicates whether request validation is enabled for this request.</summary>
 47     /// <returns>true if request validation is enabled for this request; otherwise, false. The default is true.</returns>
 48     public bool ValidateRequest
 49     {
 50         get
 51         {
 52             return this._validateRequest;
 53         }
 54         set
 55         {
 56             this._validateRequest = value;
 57         }
 58     }
 59
 60     /// <summary>Gets or sets the value provider for the controller.</summary>
 61     /// <returns>The value provider for the controller.</returns>
 62     public IValueProvider ValueProvider
 63     {
 64         get
 65         {
 66             if (this._valueProvider == null)
 67             {
 68                 this._valueProvider = ValueProviderFactories.Factories.GetValueProvider(this.ControllerContext);
 69             }
 70             return this._valueProvider;
 71         }
 72         set
 73         {
 74             this._valueProvider = value;
 75         }
 76     }
 77
 78     /// <summary>Gets the dynamic view data dictionary.</summary>
 79     /// <returns>The dynamic view data dictionary.</returns>
 80     [Dynamic]
 81     public dynamic ViewBag
 82     {
 83         [return: Dynamic]
 84         get
 85         {
 86             if (this._dynamicViewDataDictionary == null)
 87             {
 88                 this._dynamicViewDataDictionary = new DynamicViewDataDictionary(() => this.ViewData);
 89             }
 90             return this._dynamicViewDataDictionary;
 91         }
 92     }
 93
 94     /// <summary>Gets or sets the dictionary for view data.</summary>
 95     /// <returns>The dictionary for the view data.</returns>
 96     public ViewDataDictionary ViewData
 97     {
 98         get
 99         {
100             if (this._viewDataDictionary == null)
101             {
102                 this._viewDataDictionary = new ViewDtaDictionary();
103             }
104             return this._viewDataDictionary;
105         }
106         set
107         {
108             this._viewDataDictionary = value;
109         }
110     }
111     /// <summary>Executes the specified request context.</summary>
112     /// <param name="requestContext">The request context.</param>
113     /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
114     protected virtual void Execute(RequestContext requestContext)
115     {
116         if (requestContext == null)
117         {
118             throw new ArgumentNullException("requestContext");
119         }
120         if (requestContext.HttpContext == null)
121         {
122             throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
123         }
124         this.VerifyExecuteCalledOnce();
125         this.Initialize(requestContext);
126         using (ScopeStorage.CreateTransientScope())
127         {
128             this.ExecuteCore();
129         }
130     }
131
132     /// <summary>Executes the request.</summary>
133     protected abstract void ExecuteCore();
134
135     /// <summary>Initializes the specified request context.</summary>
136     /// <param name="requestContext">The request context.</param>
137     protected virtual void Initialize(RequestContext requestContext)
138     {
139         this.ControllerContext = new ControllerContext(requestContext, this);
140     }
141
142     internal void VerifyExecuteCalledOnce()
143     {
144         if (!this._executeWasCalledGate.TryEnter())
145         {
146             string message = string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, new object[]
147             {
148                 base.GetType()
149             });
150             throw new InvalidOperationException(message);
151         }
152     }
153
154     /// <summary>Executes the specified request context.</summary>
155     /// <param name="requestContext">The request context.</param>
156     void IController.Execute(RequestContext requestContext)
157     {
158         this.Execute(requestContext);
159     }
160 }

ControllerBase实现了IController接口,但是没有实现IAsyncController接口,说明ControllerBase是一个同步的基类,之所以单独又增加ControllerBase,个人认为它定义了方法调用的扩展点。ControllerBase是一个抽象类,通过显示的实现了IController的Execute方法,在这个显示实现的方法IController.Execute方法里面又调用了受保护的虚方法Execute,这个受保护的虚方法Execute又在内部调用了抽象方法ExecuteCore,作为ControllerBase的继承者,必须通过实现对抽象方法ExecuteCore来完成对Controller的执行。这个过程就是方法调用的扩展点,而且ControllerBase也抽象所有Controller中都会用到的一些共同的属性,如TempData,ControllerContext,ValueProvider,ViewBag,ViewData等,大家可以细细研究,这里就不细说了,都很简单。

在ControllerBase里面有一个初始化的方法,这个方法的作用是根据当前Controller对象和当前请求上下文RequestContext创建ControllerContext对象,在请求后续处理的时候很多地方会用到ControllerContext。

/// <summary>Initializes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
protected virtual void Initialize(RequestContext requestContext)
{
    this.ControllerContext = new ControllerContext(requestContext, this);
}

此方法是虚方法,我们可以扩展他,这个方法的调用点在受保护的虚方法Exuecute中,在调用ExecuteCore之前调用了该方法。

我们说了接口IController,IAsyncController,也说了抽象类ControllerBase,我们最后对我们要经常使用的基类做一下简述,我们真正在项目中使用的Controller的基类型是Controller类型,Controller实现了IController接口和IAsyncController接口,说明我们自己的Controller是可以同步执行,也可以异步执行的,同时实现了IDisposable接口,说明Controller类型是需要释放的,谁来释放激活的Controller的类型呢,那就是我们接下来要说的一个对象ControllerFactory,这里不多说,下面会解释清楚的。

最后我们需要说明的,虽然Controller可以同步执行,也可以异步执行,但是是同步还是异步是靠DisableAsyncSupport属性控制的,返回的是布尔值,默认情况是false,意思是指支持异步执行,以下代码说明实际情况。当BeginExecute方法执行的时候,它会根据该属性的值决定调用Execute同步执行Controller,还是调用BeginExecuteCore/EndExecuteCore方法异步执行Controller,换句话说,如果总是希望Controller同步执行,那就要把DisableAsyncSupport属性设置为true。

 1 protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
 2 {
 3             if (this.DisableAsyncSupport)
 4             {
 5                 Action action = delegate
 6                 {
 7                     this.Execute(requestContext);
 8                 };
 9                 return AsyncResultWrapper.BeginSynchronous(callback, state, action, Controller._executeTag);
10             }
11             if (requestContext == null)
12             {
13                 throw new ArgumentNullException("requestContext");
14             }
15             16             this.Initialize(requestContext);
17             BeginInvokeDelegate<Controller> beginDelegate = (AsyncCallback asyncCallback, object callbackState, Controller controller) => controller.BeginExecuteCore(asyncCallback, callbackState);
18             EndInvokeVoidDelegate<Controller> endDelegate = delegate(IAsyncResult asyncResult, Controller controller)
19             {
20                 controller.EndExecuteCore(asyncResult);
21             };
22             return AsyncResultWrapper.Begin<Controller>(callback, state, beginDelegate, endDelegate, this, Controller._executeTag, -1, null);
23 }

2、ControllerContext

在ASP.NET MVC中我们会遇到很多上下文(Context)的类型,比如:RequestContext,它是对HttpContext和RouteData的封装,今天我们看看ControllerContext,就是指Controller的上下文,再说明白一点就是Controller在执行过程中对需要用到的一些数据的封装对象,它类似一个Facade,里面包含了一些在Controller执行时所要用的一些对象,必须表示请求的HttpContextBase对象。这个类型没什么可说的,很简单,我把反编译的代码也都贴出来了,大家可以自己看。

  1 /// <summary>Encapsulates information about an HTTP request that matches specified <see cref="T:System.Web.Routing.RouteBase" /> and <see cref="T:System.Web.Mvc.ControllerBase" /> instances.</summary>
  2 public class ControllerContext
  3 {
  4     private sealed class EmptyHttpContext : HttpContextBase
  5     {
  6     }
  7
  8     internal const string ParentActionViewContextToken = "ParentActionViewContext";
  9
 10     private HttpContextBase _httpContext;
 11
 12     private RequestContext _requestContext;
 13
 14     private RouteData _routeData;
 15
 16     /// <summary>Gets or sets the controller.</summary>
 17     /// <returns>The controller.</returns>
 18     public virtual ControllerBase Controller
 19     {
 20         get;
 21         set;
 22     }
 23
 24     /// <summary>Gets the display mode.</summary>
 25     /// <returns>The display mode.</returns>
 26     public IDisplayMode DisplayMode
 27     {
 28         get
 29         {
 30             return DisplayModeProvider.GetDisplayMode(this.HttpContext);
 31         }
 32         set
 33         {
 34             DisplayModeProvider.SetDisplayMode(this.HttpContext, value);
 35         }
 36     }
 37
 38     /// <summary>Gets or sets the HTTP context.</summary>
 39     /// <returns>The HTTP context.</returns>
 40     public virtual HttpContextBase HttpContext
 41     {
 42         get
 43         {
 44             if (this._httpContext == null)
 45             {
 46                 this._httpContext = ((this._requestContext != null) ? this._requestContext.HttpContext : new ControllerContext.EmptyHttpContext());
 47             }
 48             return this._httpContext;
 49         }
 50         set
 51         {
 52             this._httpContext = value;
 53         }
 54     }
 55
 56     /// <summary>Gets a value that indicates whether the associated action method is a child action.</summary>
 57     /// <returns>true if the associated action method is a child action; otherwise, false.</returns>
 58     public virtual bool IsChildAction
 59     {
 60         get
 61         {
 62             RouteData routeData = this.RouteData;
 63             return routeData != null && routeData.DataTokens.ContainsKey("ParentActionViewContext");
 64         }
 65     }
 66
 67     /// <summary>Gets an object that contains the view context information for the parent action method.</summary>
 68     /// <returns>An object that contains the view context information for the parent action method.</returns>
 69     public ViewContext ParentActionViewContext
 70     {
 71         get
 72         {
 73             return this.RouteData.DataTokens["ParentActionViewContext"] as ViewContext;
 74         }
 75     }
 76
 77     /// <summary>Gets or sets the request context.</summary>
 78     /// <returns>The request context.</returns>
 79     public RequestContext RequestContext
 80     {
 81         get
 82         {
 83             if (this._requestContext == null)
 84             {
 85                 HttpContextBase httpContext = this.HttpContext ?? new ControllerContext.EmptyHttpContext();
 86                 RouteData routeData = this.RouteData ?? new RouteData();
 87                 this._requestContext = new RequestContext(httpContext, routeData);
 88             }
 89             return this._requestContext;
 90         }
 91         set
 92         {
 93             this._requestContext = value;
 94         }
 95     }
 96
 97     /// <summary>Gets or sets the URL route data.</summary>
 98     /// <returns>The URL route data.</returns>
 99     public virtual RouteData RouteData
100     {
101         get
102         {
103             if (this._routeData == null)
104             {
105                 this._routeData = ((this._requestContext != null) ? this._requestContext.RouteData : new RouteData());
106             }
107             return this._routeData;
108         }
109         set
110         {
111             this._routeData = value;
112         }
113     }
114
115     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class.</summary>
116     public ControllerContext()
117     {
118     }
119
120     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified controller context.</summary>
121     /// <param name="controllerContext">The controller context.</param>
122     /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext" /> parameter is null.</exception>
123     protected ControllerContext(ControllerContext controllerContext)
124     {
125         if (controllerContext == null)
126         {
127             throw new ArgumentNullException("controllerContext");
128         }
129         this.Controller = controllerContext.Controller;
130         this.RequestContext = controllerContext.RequestContext;
131     }
132
133     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified HTTP context, URL route data, and controller.</summary>
134     /// <param name="httpContext">The HTTP context.</param>
135     /// <param name="routeData">The route data.</param>
136     /// <param name="controller">The controller.</param>
137     public ControllerContext(HttpContextBase httpContext, RouteData routeData, ControllerBase controller) : this(new RequestContext(httpContext, routeData), controller)
138     {
139     }
140
141     /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified request context and controller.</summary>
142     /// <param name="requestContext">The request context.</param>
143     /// <param name="controller">The controller.</param>
144     /// <exception cref="T:System.ArgumentNullException">One or both parameters are null.</exception>
145     public ControllerContext(RequestContext requestContext, ControllerBase controller)
146     {
147         if (requestContext == null)
148         {
149             throw new ArgumentNullException("requestContext");
150         }
151         if (controller == null)
152         {
153             throw new ArgumentNullException("controller");
154         }
155         this.RequestContext = requestContext;
156         this.Controller = controller;
157     }
158 }

3、ControllerFactory

我们的Controller对象有了,为了解耦和可扩展性,ASP.NET MVC 框架中不可能直接new一个Controller的实例对象吧,我们在MvcHandler的代码中也看到了是基于接口编程的,所以需要提供一种机制,来提供我们要得到的Controller对象。既然不能直接new一个Controller出来,那我们就封装new的过程,用工厂来代替吧。对了,就是工厂,这个工厂就是ControllerFactory,从名字上可以看出它的意思,创建Controller对象的工厂。ASP.NET MVC是可以扩展的,可以自定义的,我们的ControllerFactory工厂也不例外,所以我们就产生了ControllerFactory工厂的接口,接口的名字是IControllerFactory,所有的ControllerFactory都必须实现该接口。接口定义如下:

 1       /// <summary>Defines the methods that are required for a controller factory.</summary>
 2     public interface IControllerFactory
 3     {
 4         /// <summary>Creates the specified controller by using the specified request context.</summary>
 5         /// <returns>The controller.</returns>
 6         /// <param name="requestContext">The request context.</param>
 7         /// <param name="controllerName">The name of the controller.</param>
 8         IController CreateController(RequestContext requestContext, string controllerName);
 9
10         /// <summary>Gets the controller‘s session behavior.</summary>
11         /// <returns>The controller‘s session behavior.</returns>
12         /// <param name="requestContext">The request context.</param>
13         /// <param name="controllerName">The name of the controller whose session behavior you want to get.</param>
14         SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
15
16         /// <summary>Releases the specified controller.</summary>
17         /// <param name="controller">The controller.</param>
18         void ReleaseController(IController controller);
19     }

IControllerFactory接口是对管理Controller对象的工厂的抽象,怎么管理呢,无非就是我要创建一个Controller就能创建一个Controller。我们创建好了Controller,不使用的情况就要能销毁它,能创建和能销毁就构成了IControllerFactory的主要功能,CreateController用于创建Controller对象实例,ReleaseController方法用于销毁不用的Controller对象,因为我们的Controller实现了IDisposable接口。想起来了吗?因为我们自定义的Controller都继承了抽象基类Controller类型,该抽象Controller基类实现了IDisposable接口。

IControllerFactory接口还有一个方法GetControllerSessionBehavior,用于管理Controller的会话状态,返回的类型为SessionStateBehavior枚举值,它有四个枚举值:Default(使用默认的ASP.NET逻辑来确定请求的会话行为),Required(为请求启用完全的读和写会话状态的行为),ReadOnly(为请求启用只读的会话状态),Disabled(禁用会话状态)。说起来话长,在System.Web.SessionState命名空间下有两个接口定义,分别是IRequiresSessionState(实现本接口,HttpHandler采用Required会话模式)和IReadOnlySessionState(实现本接口,HttpHandler采用ReadOnly会话模式)类型,这个两个接口是标记接口,没有定义具体的方法。我们在看看我们的MvcHandler类型的定义吧,代码如下:

public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState

具体采用何种会话状态行为取决于当前Http请求上下文(HttpContext的静态属性Current表示的对象),对于ASP.NET 3.0及其以前的版本,我们不能对当前的Http上下文的会话状态行为模式进行修改,在ASP.NET 4.0中为HttpContext对象定义了一个SetSessionStateBehavior方法,此方法也在HttpContextBase定义了,HttpContextBase的子类HttpContextWrapper重写了这个方法,在内部调用HttpContext的同名方法来设置当前请求的会话状态模式。

4、ControllerBuilder

我们有了Controller,可以通过IController,IAsyncController,ControllerBase或者Controller抽象基类来自定义我们自己的Controller基类型,我们也有了IControllerFactory接口,可以管理Controller对象,也可以定义我们自己的ControllerFactory来取代系统的默认实现。问题来了,我们自定义了ControllerFactory对象,但是如何把我们自定义的ControllerFactory放到ASP.NET MVC框架中呢,那就需要我们这个类型了,它的名字就是ControllerBuilder。

代码最有说服力,我们先上代码吧,源码如下:

  1      /// <summary>Represents a class that is responsible for dynamically building a controller.</summary>
  2     public class ControllerBuilder
  3     {
  4         private static ControllerBuilder _instance = new ControllerBuilder();
  5
  6         private Func<IControllerFactory> _factoryThunk = () => null;
  7
  8         private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  9
 10         private IResolver<IControllerFactory> _serviceResolver;
 11
 12         /// <summary>Gets the current controller builder object.</summary>
 13         /// <returns>The current controller builder.</returns>
 14         public static ControllerBuilder Current
 15         {
 16             get
 17             {
 18                 return ControllerBuilder._instance;
 19             }
 20         }
 21
 22         /// <summary>Gets the default namespaces.</summary>
 23         /// <returns>The default namespaces.</returns>
 24         public HashSet<string> DefaultNamespaces
 25         {
 26             get
 27             {
 28                 return this._namespaces;
 29             }
 30         }
 31
 32         /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerBuilder" /> class.</summary>
 33         public ControllerBuilder() : this(null)
 34         {
 35         }
 36
 37         internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
 38         {
 39             IResolver<IControllerFactory> arg_6A_1 = serviceResolver;
 40             if (serviceResolver == null)
 41             {
 42                 arg_6A_1 = new SingleServiceResolver<IControllerFactory>(() => this._factoryThunk(), new DefaultControllerFactory
 43                 {
 44                     ControllerBuilder = this
 45                 }, "ControllerBuilder.GetControllerFactory");
 46             }
 47             this._serviceResolver = arg_6A_1;
 48         }
 49
 50         /// <summary>Gets the associated controller factory.</summary>
 51         /// <returns>The controller factory.</returns>
 52         public IControllerFactory GetControllerFactory()
 53         {
 54             return this._serviceResolver.Current;
 55         }
 56
 57         /// <summary>Sets the specified controller factory.</summary>
 58         /// <param name="controllerFactory">The controller factory.</param>
 59         /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerFactory" /> parameter is null.</exception>
 60         public void SetControllerFactory(IControllerFactory controllerFactory)
 61         {
 62             if (controllerFactory == null)
 63             {
 64                 throw new ArgumentNullException("controllerFactory");
 65             }
 66             this._factoryThunk = (() => controllerFactory);
 67         }
 68
 69         /// <summary>Sets the controller factory by using the specified type.</summary>
 70         /// <param name="controllerFactoryType">The type of the controller factory.</param>
 71         /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerFactoryType" /> parameter is null.</exception>
 72         /// <exception cref="T:System.ArgumentException">The controller factory cannot be assigned from the type in the <paramref name="controllerFactoryType" /> parameter.</exception>
 73         /// <exception cref="T:System.InvalidOperationException">An error occurred while the controller factory was being set.</exception>
 74         public void SetControllerFactory(Type controllerFactoryType)
 75         {
 76             if (controllerFactoryType == null)
 77             {
 78                 throw new ArgumentNullException("controllerFactoryType");
 79             }
 80             if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType))
 81             {
 82                 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_MissingIControllerFactory, new object[]
 83                 {
 84                     controllerFactoryType
 85                 }), "controllerFactoryType");
 86             }
 87             this._factoryThunk = delegate
 88             {
 89                 IControllerFactory result;
 90                 try
 91                 {
 92                     result = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
 93                 }
 94                 catch (Exception innerException)
 95                 {
 96                     throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_ErrorCreatingControllerFactory, new object[]
 97                     {
 98                         controllerFactoryType
 99                     }), innerException);
100                 }
101                 return result;
102             };
103         }
104     }

源码不是很多,我贴出了完整的代码。

ControllerBuilder定义了一个静态只读属性Current,返回当前使用的ControllerBuilder对象。该类型有一个返回类型为IControllerFactory的方法GetControllerFactory,这个方法用于获取当前注册的ControllerFactory对象。ControllerBuilder类型还有两个重载的SetControllerFactory方法,这两个方法的主要作用是把我们自定义的ControllerFactory注册到ASP.NET MVC框架中去。但是这两个方法有些不同,参数类型为IControllerFactory的SetControllerFactory方法直接注册ControllerFactory实例对象,而参数类型为Type的SetControllerFactory方法注册的ControllerFactory的类型。

如果我们注册的是ControllerFactory类型的话,那么GetControllerFactory方法在获取ControllerFactory对象的时候都要通过反射来获得,也就是说针对GetControllerFactory方法的每次调用都会伴随着ControllerFactory的实例化,ASP.NET MVC框架不会对实例化的ControllerFactory进行缓存。如果我们注册的是ControllerFactory对象实例,针对GetControllerFactory方法来说是直接返回注册的对象,就性能而言,注册ControllerFactory对象比较好。

命名空间

       如果在多个命名空间下定义了多个同名的Controller类型,如果只是按着名称来匹配就会导致激活系统无法确定具体的Controller的类型而抛出异常。为了解决这个问题,我们必须为定义了同名的Controller类型的命名空间设置不同的优先级。

ASP.NET MVC的Controller的激活系统为我们提供了两种提升命名空间优先级的方法。第一种方法就是通过调用RouteCollectionExtensions的扩展方法MapRoute的时候提供命名空间列表。此种方式指定的命名空间列表会保存在Route路由对象的DataTokens属性中,对应的Key是Namespaces。第二种方式就是把命名空间列表添加到当前的ControllerBuilder类型的默认命名空间列表中。这些命名空间列表存放在返回类型为HashSet<string>的DefaultNamespaces属性中。这两种方法的优先级第一种方法更高。

对于Area的命名空间来说,如果我们在调用AreaRegistrationContext对象的MapRoute方法时提供了命名空间,该命名空间会作为Route对象的命名空间,如果没有提供,则AreaRegistration类型所在的命名空间,再加上“.*”作为Route对象的命名空间。当我们调用AreaRegistrationContext的MapRoute方法的时候,会在Route对象的DataTokens属性里增加一个Key为UseNamespaceFallback的变量,它表示是否采用后备的命名空间来解析Controller类型。如果Route对象有命名空间,该值就是False,否则就是true。

解析过程是,ASP.NET MVC会先使用RouteData包含的命名空间,如果解析失败,它会从RouteData对象的DataTokens属性中取出Key为UseNamespaceFallback的值,如果该值为true或者不存在,就使用ControllerBuidler的默认命名空列表来解析。如果UseNamespaceFallback的值为false,就不实用后备的命名空间,如果没找到就会抛出异常。

三、激活Controller类型的缓存和释放

一个比较大的系统里面,可能所涉及到的Controller类型成千上万,如果每次都要通过反射的方式及时获取,那对性能的影响是很客观的,ASP.NET MVC框架组想到了这一点,针对Controller和AreaRegistration都实现了彻底的缓存,这个缓存是把所有解析出来的类型持久化到物理文件中,两个物理文件的名称分别是:MVC-ControllerTypeCache.xml和MVC-AreaRegistrationTypeCache.xml,具体的目录在%windir%\Microsoft.Net\Framework\v{version}\Temporary ASP.NET Files\root\..\..\UserCache\。

当接收到web应用被启动后的第一个请求的时候,Controller激活系统会读取这些文件,通过反序列化生成List<Type>对象。

MVC-ControllerTypeCache.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>

<!--This file is automatically generated. Please do not modify the contents of this file.-->

-<typeCache mvcVersionId="cc73190b-ab9d-435c-8315-10ff295c572a" lastModified="2017/6/23 18:34:17">

-<assembly name="MVCTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">

-<module versionId="1949a001-c30a-4d56-ac65-d1714f608b76">

<type>MVCTest.Controllers.AccountController</type>

<type>MVCTest.Controllers.HomeController</type>

<type>MVCTest.Controllers.ManageController</type>

</module>

</assembly>

</typeCache>

Controller的释放很简单,直接上代码吧。

/// <summary>Releases the specified controller.</summary>
/// <param name="controller">The controller to release.</param>
public virtual void ReleaseController(IController controller)
{
    IDisposable disposable = controller as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

由于所有的Controller类型都实现了IDisposable接口,所以我们可以直接调用当前Controller对象的Dispose方法即可。

四、小结

   好了,这篇文章就先写到这里吧,Controller的激活系统还没有完,今天只是从总体上来讲一下,内容很多,很难在一片文章里把所有东西都写到。这个概览先给大家一个整体上的感觉吧,下一篇文章就具体写一下Controller的激活解析过程。

写一写很不错,把自己的理解写出来,现在更清晰了,也希望高手来指正和赐教。

时间: 2024-10-26 03:40:24

白话ASP.NET MVC之二:Controller激活系统的概览的相关文章

白话ASP.NET MVC之三:Controller是如何解析出来的

我们在上一篇文章中介绍Controller激活系统中所涉及到的一些类型,比如有关Controller类型的相关定义类型就包括了IController类型,IAsyncController类型,ControllerBase抽象类型和我们最终要使用的抽象类型Controller,这是ASP.NET MVC 框架中和Controller本身定义相关的类型.其他辅助类型,包括管理Controller的类型ControllerFactory,这个工厂负责Controller的生产和销毁.我们还涉及到另一个

ASP.NET MVC 控制器(二)

ASP.NET MVC 控制器激活(二) 前言 在之前的篇幅中,用文字和图像来表示了控制器的激活过程,描述的角度都是从框架默认实现的角度去进行描述的,这样也使得大家都可以清楚的知道激活的过程以及其中涉及到的对象模型,今天的篇幅就是在激活的过程中,框架提供了哪些可注入点,站在一个使用者的角度来进行描述. 激活控制器-注入点入口 如上图,这是上个篇幅中描述的控制器激活过程图,这里引用过来是怕有的朋友忘记了前面的所说和没看过前面篇幅的朋友. 就从默认控制器工厂的实现来看,在CreateControll

ASP.NET MVC路由(二)

 ASP.NET MVC路由(二) 前言 在上一篇中,提及了Route.RouteCollection对象的一些信息,以及它们的结构所对应的关系.按照处理流程走下来还有遗留的疑问没有解决这个篇幅就来讲解一下. URL规则的生成 Url规则看名字挺吓唬人的,其实就是根据我们自定义的Url来解析出一个模式,然后等待请求的Url来的时候,跟我们定义的模式进行匹配(如下图).这是后续的内容. 在上篇中说到URL规则的定义是在Route对象中的,下面来讲解在Route对象中怎么根据用户注册的URL转变成U

[转]深入ASP.NET MVC之二:路由模块如何工作

本文转自:http://www.cnblogs.com/yinzixin/archive/2012/11/05/2754483.html 摘要: 上文分析了UrlRouting模块何时会被触发,本文重点分析路由模块是如何工作,以及如何利用路由模块实现Area. 先看路由模块的PostResolveRequestCache事件中被触发的方法: public virtual void PostResolveRequestCache(HttpContextBase context) { RouteDa

ASP.NET MVC 视图(二)

ASP.NET MVC 视图(二) 前言 上篇中对于视图引擎只是做了简单的演示,对于真正的理解视图引擎的工作过程可能还有点模糊,本篇将会对由MVC框架提供给我们的Razor视图引擎的整个执行过程做一个粗略的讲解,目的在于让大家对Razor视图引擎的执行过程留个印象以便联想的思考到视图引擎的作用以及视图在MVC框架中的表示. ASP.NET MVC 视图 自定义视图引擎 Razor视图引擎执行过程 Razor视图的依赖注入.自定义视图辅助器 分段.分部视图的使用 Razor语法.视图辅助器 Raz

ASP.NET MVC中 在controller 里将 Partial View 转化为字符串的方法

namespace Common.Helper { public static class ControllerExtension { //根据部分视图名称,把部分视图内容转换成字符串 public static string RenderPartialViewToString(this Controller controller, string partialViewName) { return controller.RenderPartialViewToString(partialViewN

ASP.NET MVC Controller激活系统详解2

一.引言 此篇博文紧接上篇博文进行阐述,本篇博文阐述的主题是Controller激活和url路由 二.总述 ASP.NET路由系统是HTTP请求抵达服务端的第一道屏障,它根据注册的路由规则对拦截的请求进行匹配并解析包含目标的Controller和Action名称的路由信息.当前ControllerBuilder具有用于激活Controller对象的ControllerFactory. ASP.NET路由系统的核心是一个叫做UrlRoutingModule的自定义HttpModule,路由的实现是

ASP.NET MVC进阶二

一.数据验证 数据验证的步骤 在模型类中添加与验证相关的特性标记 在客户端导入与验证相关的js文件和css文件 使用与验证相关的Html辅助方法 在服务器端判断是否通过服务器端验证 常用的验证标记 Required:非空验证 StringLength:验证字符串的长度 RegularExpression:正则表达式验证 Compare:比较两个字段的值是否相等 Range:范围验证 Remote:服务器验证(需要在controller中编写返回值为JsonResult的Action) 自定义验证

ASP.NET MVC中的控制器激活与反射之间的联系(帮助理解)

ASP.NET Mvc是ASP.NET的一个框架,同样也是基于管道的设计结构.HttpModule和HttpHandler是ASP.NET的两个重要组件,同样的在Mvc中也是非常重要的组件.在应用程序中的管道设计结构下实际上是由一系列的事件组合在一起的.这些事件可以有HttpModule来订阅,订阅的时机是在应用程序加载配置文件的时候,订阅web.config中httomodules配置的相关事件.而HttpHandler的作用就是最终的真正执行. 激活前准备首先要说的是路由系统一个Web应用程