[MVC]ASP.NET MVC Preview 2 - 流程分析 (2)

mikel阅读(773)

这次探险历程将从 MvcHandler 开始。

public class MvcHandler : IHttpHandler, IRequiresSessionState
{
  protected virtual void ProcessRequest(HttpContext httpContext)
  {
    HttpContextBase base2 = new HttpContextWrapper2(httpContext);
    this.ProcessRequest(base2);
  }
  protected internal virtual void ProcessRequest(HttpContextBase httpContext)
  {
    string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
    IControllerFactory controllerFactory = this.ControllerBuilder.GetControllerFactory();
    IController controller = controllerFactory.CreateController(this.RequestContext, requiredString);
    if (controller == null)
    {
      throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,
        MvcResources.ControllerBuilder_FactoryReturnedNull,
        new object[] { controllerFactory.GetType(), requiredString }));
    }
    
    try
    {
      ControllerContext controllerContext = new ControllerContext(this.RequestContext, controller);
      controller.Execute(controllerContext);
    }
    finally
    {
      controllerFactory.DisposeController(controller);
    }
  }
}

首 先从 RouteData 中提取 Controller 的名字(这个名字是我们在 Global.asax.cs RegisterRoutes 中注册 Route 时提供的),然后获取 ControllerFactory,只不过这里面专门提供了一个 ControllerBuilder。

internal ControllerBuilder ControllerBuilder
{
  get
  {
    if (this._controllerBuilder == null)
    {
      this._controllerBuilder = ControllerBuilder.Current;
    }
    return this._controllerBuilder;
  }
  set
  {
    this._controllerBuilder = value;
  }
}

除非我们自定义 MvcHandler,否则就直接去看看这个 ControllerBuilder.Current 吧。

public class ControllerBuilder
{
  // Fields
  private Func<IControllerFactory> _factoryThunk;
  private static ControllerBuilder _instance = new ControllerBuilder();
  // Methods
  public ControllerBuilder()
  {
    this.SetControllerFactory(new DefaultControllerFactory());
  }
  public IControllerFactory GetControllerFactory()
  {
    return this._factoryThunk();
  }
  public void SetControllerFactory(Type controllerFactoryType)
  {
    // ….
    this._factoryThunk = delegate
    {
      IControllerFactory factory;
      
      try
      {
        factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
      }
      catch (Exception exception)
      {
        // …
      }
      return factory;
    };
  }
  public void SetControllerFactory(IControllerFactory controllerFactory)
  {
    // …
    this._factoryThunk = () => controllerFactory;
  }
  // Properties
  public static ControllerBuilder Current
  {
    get { return _instance; }
  }
}

嗯,有点意思。剔除掉无关紧要的代码,这个 ControllerBuilder 告诉我们如下事实。
(1) 通常情况下,返回一个默认的 DefaultControllerFactory 实例。
(2) 我们可以在 Application_Start 中通过 ControllerBuilder.Current.SetControllerFactory 方法来注册一个我们自定义的工厂。
(3) 核心代码: factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
好 了,回到前面的流程。MvcHandler.ProcessRequest 是通过 DefaultControllerFactory.CreateController(RequestContext, requiredString) 来返回 IController 实例。那么我们就看看这个 DefaultControllerFactory.CreateController 又有什么玄机。

public class DefaultControllerFactory : IControllerFactory
{
  protected internal virtual IController CreateController(RequestContext requestContext, string controllerName)
  {
    //…
    this.RequestContext = requestContext;
    Type controllerType = this.GetControllerType(controllerName);
    return this.GetControllerInstance(controllerType);
  }
  protected internal virtual Type GetControllerType(string controllerName)
  {
    Type type2;
    
    // …
    if (!this.ControllerTypeCache.Initialized)
    {
      // … 自己打开 Reflector 看吧 …
    }
    if (!this.ControllerTypeCache.TryGetControllerType(controllerName, out type2))
    {
      return null;
    }
    
    // …
    return type2;
  }
  protected internal virtual IController GetControllerInstance(Type controllerType)
  {
    IController controller;
    //…
    try
    {
      controller = Activator.CreateInstance(controllerType) as IController;
    }
    catch (Exception exception)
    {
      // …
    }
    return controller;
  }
}

这 个实在没啥好说的,通过反射来创建 Controller 实例。唯一有点看头的就是 GetControllerType 里面做了些缓存处理,以此来避免频繁使用反射造成的性能问题。回到 MvcHandler.ProcessRequest(),在得到控制器实例后,MvcHandler 开始了调用 Controller.Execute() 来进一步触发后续操作,同时对其上下文进一步封装,除了前面创建的 RequestContext,还加上了当前这个 Controller 对象的引用。

public class Controller : IController
{
  protected internal virtual void Execute(ControllerContext controllerContext)
  {
    //…
    string requiredString = this.RouteData.GetRequiredString("action");
    if (!this.InvokeAction(requiredString))
    {
      this.HandleUnknownAction(requiredString);
    }
  }
}

获取 Action 的名字,然后开始执行 InvokeAction。

protected internal bool InvokeAction(string actionName)
{
  return this.InvokeAction(actionName, new RouteValueDictionary());
}
protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values)
{
  // …
  MemberInfo[] infoArray = base.GetType().GetMember(actionName, MemberTypes.Method, …);
  MethodInfo methodInfo = null;
  
  foreach (MemberInfo info2 in infoArray)
  {
    MethodInfo info3 = (MethodInfo)info2;
    if ((!info3.IsDefined(typeof(NonActionAttribute), true) && !info3.IsSpecialName) &&
      info3.DeclaringType.IsSubclassOf(typeof(Controller)))
    {
      if (methodInfo != null)
      {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture,
          MvcResources.Controller_MoreThanOneAction,
          new object[] { actionName, base.GetType() }));
      }
      methodInfo = info3;
    }
  }
  
  if (methodInfo != null)
  {
    this.InvokeActionMethod(methodInfo, values);
    return true;
  }
  return false;
}

这 段代码还是很有趣的。首先,它通过反射获取所有同名 Action 方法信息;其次,它过滤掉所有有 NonActionAttribute 和 IsSpecialName 标记的方法;第三,当同名有效 Action 被重载时它会抛出异常 —— 提示 "Controller_MoreThanOneAction"。
也就是说下面的写法是可以的。

public class HomeController : Controller
{
  public void About()
  {
    About(123);
  }
  [NonAction]
  public void About(int x)
  {
    Response.Write("x…");
  }
}

可一旦去掉那个 "[NonAction]",你将看到如下的异常信息。

More than one action named 'About' was found on controller 'Rainsoft.Web.Controllers.HomeController'.

继续 InvokeActionMethod,这个方法看上去很复杂,其实最核心的代码就最后一行,前面都是相关参数的分解。

protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values)
{
  // …
  this.InvokeActionMethodFilters(methodInfo, delegate
  {
    methodInfo.Invoke(this, parameterValues);
  });
}

这行代码将 Action 的调用作为一个委托,连同反射信息传递给 InvokeActionMethodFilters。

private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation)
{
  List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>
  {
    new ControllerActionFilter(this)
  };
  Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
  for (Type type = base.GetType(); type != null; type = type.BaseType)
  {
    memberChain.Push(type);
  }
  List<ActionFilterAttribute> collection = SortActionFilters(memberChain);
  filters.AddRange(collection);
  List<ActionFilterAttribute> list3 = PrepareMethodActionFilters(methodInfo);
  filters.AddRange(list3);
  
  FilterContext context = new FilterContext(this.ControllerContext, methodInfo);
  new ActionFilterExecutor(filters, context, continuation).Execute();
}

这 个方法首先将默认的过滤器 ControllerActionFilter 加到列表,然后提取所有继承层次上基类的过滤器特性。最后将这些过滤器集合、过滤上下文,连同前一个方法传递进来的 Action 执行委托(continuation) 再次转交给了一个 ActionFilterExecutor 对象实例,并调用其 Execute 方法。

internal sealed class ActionFilterExecutor
{
  // Fields
  private FilterContext _context;
  private Action _continuation;
  private List<ActionFilterAttribute> _filters;
  // Methods
  public ActionFilterExecutor(List<ActionFilterAttribute> filters, FilterContext context, Action continuation)
  {
    this._filters = filters;
    this._context = context;
    this._continuation = continuation;
  }
  public void Execute()
  {
    IEnumerator<ActionFilterAttribute> enumerator = this._filters.GetEnumerator();
    this.ExecuteRecursive(enumerator);
  }
  private FilterExecutedContext ExecuteRecursive(IEnumerator<ActionFilterAttribute> enumerator)
  {
01:    if (enumerator.MoveNext())
02:    {
03:      ActionFilterAttribute current = enumerator.Current;
04:      FilterExecutingContext filterContext = new FilterExecutingContext(this._context);
05:      current.OnActionExecuting(filterContext);
06:
07:      if (filterContext.Cancel)
08:      {
09:        return new FilterExecutedContext(this._context, null);
10:      }
11:
12:      bool flag = false;
13:      FilterExecutedContext context2 = null;
14:      try
15:      {
16:        context2 = this.ExecuteRecursive(enumerator);
17:      }
18:      catch (Exception exception)
19:      {
20:        flag = true;
21:        context2 = new FilterExecutedContext(this._context, exception);
22:        current.OnActionExecuted(context2);
23:        if (!context2.ExceptionHandled)
24:        {
25:          throw;
26:        }
27:      }
28:
29:      if (!flag)
30:      {
31:        current.OnActionExecuted(context2);
32:      }
33:      return context2;
34:    }
35:
36:    this._continuation();
37:    return new FilterExecutedContext(this._context, null);
  }
}

ExecuteRecursive 看上去不大容易理解。不过对于习惯使用递归的人来说,也不是什么难事。
(1) 通过迭代器 MoveNext() 方法提取一个过滤器对象,执行其 OnActionExecuting 方法。
(2) 如果该方法设置了 filterContext.Cancel = true,则放弃后续执行代码。这种机制为我们提供了更好的控制(Preview 1 通过 Context.OnPreAction 的返回值来做同样的控制,现已废弃),比如写一个 CacheFilterAttribute,在缓存未过期时直接输出静态内容,并终止后续执行。(我会在后面章节,提供一个可用的缓存过滤器代码)
(3) 进入第 16 行递归调用,这样一来,就可以一层一层调用所有的 ActionFilterAttribute.OnActionExecuting 方法,直到 MoveNext() == false。
(4) 在最后一次递归调用时,由于 enumerator.MoveNext() == false,故 36 行的 _continuation() 方法得以被执行。还记得这个方法吗?就是前面给传递过来的 Action 方法委托,这意味着我们写的 Action 方法总算是执行了。这个方法是我们这一章流程分析的终点。
(5) 在 Action 委托执行完成后,递归调用自 29 行恢复,逐级往上回溯,直到最初那个方法堆栈。这样所有 ActionFilterAttribute.OnActionExecuted 也被执行完成。
好了,到此为止,我们基本完成了 "核心" 部分的流程分析过程。虽然简单了些,但对于我们理解 ASP.NET MVC 执行机制和原理还是很有必要的。下一章的分析,就得从那个 Action Delegate 开始了。
—————–
附: 流程分析图

uploads/200803/11_193520_mvc2.png

查看大图

[C#]VS2008中查看.NET源码的设置方法

mikel阅读(1003)

    在Visual Studio 2008中可以通过调试进入.NET Framework的源代码,从这个意义上说,.NET Framework是开放部分源代码了,但现在只支持调试模式下进入源代码。而其,这个功能在Visual Studio 2008 Express版本中不能支持。注意,所有的源代码都是从指定的远程服务器上按需下载的,而不是VS2008安装后就自带的。
    要想使用这个功能,需要额外的配置一下,因为默认配置中VS2008是不开启这个功能的,那么如何配置呢?在Scott的Blog中有专门的说明,但是他的源代码服务器URL给错了。收听了一下他们的采访视频,确定了正确配置的方式,如下:
    在VS2008中,菜单Tools->Options后,
   (1)如果你在VB Profile环境下运行,需要将左下角的Show All Settings勾选上(否则将不会出现Debugging选项),然后选择Debugging->General。
   (2)如果你不是在VB Profile下,则直接选择Debugging->General。
   (3)如图:去掉“Enable Just My Code”的勾选。代表不仅仅只是调试我们自己开发的代码(言外之意也要调试.NET Framework的源代码)
   (4)如图:打开“Enable Source Server Support”勾选。代表开启源代码服务器的支持(言外之意在需要的时候去源码服务器获取.NET Framwork的源代码)

111111111111111.gif

   (5)选择“Debugging->Symbols”页,然后设置符号下载URL和缓存位置。设置为:http://referencesource.microsoft.com/symbols 
   (6)设置符号的本地缓存位置。如:C:\temp\symbols。注意确保你的Windows账户能读写这个位置。
   (7)打开“Search the above locations only when symbols are loaded manually”选项。表明只有当symbols被手动加载得情况下才使用。在这种模式下,第一次进入需要进入.NET Framework中的某一个dll时,需要手动执行Load Symbols操作(注:每个dll只有一次,之后它就被缓存到(6)所设置的本地缓存中了)。如果嫌麻烦,这里也提供了一个简单的方法,即勾选“Load symbols using the updated setting when this dialog is closed”,表明当这个设置窗口关闭的时候,立即下载所有的symbols(这将需要几分钟~几十分钟,根据网速决定),这样就不用以后再手动Load symbols操作了。

111111111111111.gif

    通过以上的设置后,在调试程序的时候就可以进入框架的源代码了。通过例子看一下,创建这个工程,并设置个断点。运行程序到断点停止时,到调用堆栈窗口 (CTRL+ALT+C)右键单击一个dll(如:System.Windows.Forms.dll),然后选择“Load Symbols”,这样会给System.Windows.Forms程序集加载符号。注意:如果在配置过程中采用了当时一次性下载了所有的 symbols的话,这里的Load Symbols可以省略不做。

111111111111111.gif

    现在可以查看代码了。你可以Step In(F11)上面的的代码行,在第一次进入代码的时候,我们会显示EULA,点击ACCEPT,然后这个源代码就会下载下来,可以调试.net框架源代码了。

111111111111111.gif 
 

通过这种方式查看的源代码并非全部,这次发布的版本中包含的命名空间有:

◆System
◆System.CodeDom
◆System.Collections
◆System.ComponentModel
◆System.Data
◆System.Diagnostics
◆System.Drawing
◆System.Globalization
◆System.IO
◆System.Net
◆System.Reflection
◆System.Runtime
◆System.Security
◆System.Text
◆System.Threading
◆System.Web
◆System.Web.Extensions
◆System.Windows
◆System.Windows.Forms
◆System.Xml

[MVC]ASP.NET MVC Preview 2 - 流程分析 (1)

mikel阅读(765)

MVC 通过在 web.config 中添加相应的 Module 来参与 ASP.NET 执行流程。

<httpModules>
  <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing …" />
</httpModules>

那么,这个 UrlRoutingModule 自然就是我们分析过程的入口。

public class UrlRoutingModule : IHttpModule
{
  protected virtual void Init(HttpApplication application)
  {
    application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
    application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
  }  
}

订阅了两个 HttpApplication 事件~~~~ 嗯,PostResolveRequestCache 要比 PostMapRequestHandler 更早执行,先从它开始吧。

private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
  HttpContextBase context = new HttpContextWrapper2(((HttpApplication) sender).Context);
  this.PostResolveRequestCache(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(…);
    }
    RequestContext requestContext = new RequestContext(context, routeData);
    IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    if (httpHandler == null)
    {
      throw new InvalidOperationException(…);
    }
    context.Items[_requestDataKey] = new RequestData { originalPath = context.Request.Path, HttpHandler = httpHandler };
    context.RewritePath("~/UrlRouting.axd");
  }
}

首先从 RouteCollection 中获取一个 RouteData 对象,RouteCollection 何许人也?

public RouteCollection RouteCollection
{
  get
  {
    if (this._routeCollection == null)
    {
      this._routeCollection = RouteTable.Routes;
    }
    return this._routeCollection;
  }
  set
  {
    this._routeCollection = value;
  }
}

说白了,所谓 RouteCollection 就是我们在 Global.asax.cs 中添加的 Route 集合。

public static void RegisterRoutes(RouteCollection routes)
{
  // Note: Change the URL to "{controller}.mvc/{action}/{id}" to enable
  // automatic support on IIS6 and IIS7 classic mode
  routes.Add(new Route("{controller}/{action}/{id}", new MvcRouteHandler())
  {
    Defaults = new RouteValueDictionary(new { action = "Index", id = "" }),
  });
  routes.Add(new Route("Default.aspx", new MvcRouteHandler())
  {
    Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),
  });
}

回 到上文,在获取 RoteCollection 之后,通过调用 GetRouteData(context) 返回一个 RouteData 对象(详细代码可阅读 Route.GetRouteData 方法,它通过分析当前上下文的相关请求参数,返回所需的结果对象),该对象内部包含了我们注册 Route 时的相关设置,当然就有下面所需要的 —— MvcRouteHandler。
接下来,该方法将 routeData 和上下文一起打包成 RequestContext,这应该是给后面相关处理准备的上下文环境。通过调用 MvcRouteHandler.GetHttpHandler() 方法,我们终于看到了曙光,也就是进行后续流程的关键 —— MvcHandler (有关 MvcHandler 的细节,后续章节再说)。

public class MvcRouteHandler : IRouteHandler
{
  protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    return new MvcHandler(requestContext);
  }
}

到了这一步,MVC 框架已经准备好了相应的执行场景,接下来就是修改默认的执行流程了。当然,辛苦得来的 "环境" 要选择一个合适的 "人",存储到一个合适的 "地方"。

context.Items[_requestDataKey] = new RequestData { originalPath = context.Request.Path, HttpHandler = httpHandler };

而 后的某个时间,OnApplicationPostMapRequestHandler 被执行。在 PostMapRequestHandler 中,它提取了前面预先 "埋" 下的 "坏蛋",并修改了 HttpContext.Handler,使得 MvcHandler 能继续未完的 "事业"。至于跳转之类的,就无需我再多说。

private void OnApplicationPostMapRequestHandler(object sender, EventArgs e)
{
  HttpContextBase context = new HttpContextWrapper2(((HttpApplication) sender).Context);
  this.PostMapRequestHandler(context);
}
public virtual void PostMapRequestHandler(HttpContextBase context)
{
  RequestData data = (RequestData) context.Items[_requestDataKey];
  if (data != null)
  {
    context.RewritePath(data.OriginalPath);
    context.Handler = data.HttpHandler;
  }
}

待续……
—————————-
附上流程分析图

uploads/200803/11_155627_mvc1.png

查看大图

[MVC]ASP.NET MVC beta 模板(Templates) 中文修正补丁 完整版

mikel阅读(668)

ASP.NET MVC beta 模板(Templates) 中文修正补丁 完整版

 

2008年10月15日 发布的 ASP.NET MVC beta ,安装后,在VS2008中模板(Template)没有正确显示。

此补丁解决此问题。方便一起学习ASP.NET MVC 的朋友~

需要安装新版本,或者 需要卸载 ASP.NET MVC beta 请先使用恢复功能!~

 

2008年12月28日 补充:

非常抱歉,做了一件事却没有认真去做好。之前上传了一份补丁,却只修正了C#的部分模板。

这次重新发布了此补丁,可以修正C# VB 及Test 的模板~ 希望能给大家带来方便~!

下载:ASP.NET MVC beta 模板(Templates) 中文修正补丁 完整版

[MVC]ASP.NET MVC Preview 3 流程分析 - 4.Router

mikel阅读(783)

我们先了解一下 System.Web.Routing 中的几个类型。

  • UrlRoutingModule : IHttpModule : 入口点。订阅 HttpApplication 相关事件,修改 HttpContext.Handler (IHttpHandler),使得 MvcHandler 得以执行。
  • Route : RouteBase : 路径转向规则,包括路径匹配表达式(Url)、参数默认值(Defaults)、请求处理程序(RouteHandler) 等。
  • RouteCollection : Collection<RouteBase> : 存储所有的 Route 规则的集合,提供依据上下文获取动态路由数据的方法。
  • RouteTable : 持有全局 RouteCollection 实例引用的一个辅助类。
  • RouteData : 依据当前上下文进行解析的动态请求路由信息,包括从请求参数或路由规则中提取的 Controller Name、Action Name、Action Method Parameter 等。
  • IRouteHandler : Route 中请求处理程序的包装接口。

要使用 Routing,除了在配置文件中添加 UrlRoutingModule 外,我们还得在 Global.asax.cs 中注册相应的路由规则(Route)。

public class GlobalApplication : System.Web.HttpApplication
{
  public static void RegisterRoutes(RouteCollection routes)
  {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
      "Test",
      "Home/Test/{x}/{y}",
      new { controller = "Home", action = "Test", x = 100, y = 200 }
    );
    routes.MapRoute(
      "Default", // Route name
      "{controller}/{action}/{id}", // URL with parameters
      new { controller = "Home", action = "Index", id = "" } // Parameter defaults
    );
  }
  protected void Application_Start()
  {
    RegisterRoutes(RouteTable.Routes);
  }
}

System.Web.Mvc 额外提供了几个扩展方法来简化我们注册 Route 规则。

public static class RouteCollectionExtensions
{
  public static void IgnoreRoute(this RouteCollection routes, string url)
  {
    routes.IgnoreRoute(url, null);
  }
  public static void IgnoreRoute(this RouteCollection routes, string url, object constraints)
  {
    // … 省略部分代码 …
    Route route = new Route(url, new StopRoutingHandler())
    {
      Constraints = new RouteValueDictionary(constraints)
    };
    routes.Add(route);
  }
  public static void MapRoute(this RouteCollection routes, string name, string url)
  {
    routes.MapRoute(name, url, null, null);
  }
  public static void MapRoute(this RouteCollection routes, string name, string url, object defaults)
  {
    routes.MapRoute(name, url, defaults, null);
  }
  public static void MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
  {
    // … 省略部分代码 …
    Route route = new Route(url, new MvcRouteHandler())
    {
      Defaults = new RouteValueDictionary(defaults),
      Constraints = new RouteValueDictionary(constraints)
    };
    if (string.IsNullOrEmpty(name))
    {
      routes.Add(route);
    }
    else
    {
      routes.Add(name, route);
    }
  }
}

有一点需要注意,System.Web.Routing 通过循环查找的方式来匹配路由规则,因为我们要按以下顺序注册路由规则。
(1) 忽略规则 (IgnoreRoute)。
(2) 具体匹配规则。
(3) 通用匹配规则。
RouteTable.Routes 实际上指向一个应用程序域中唯一的 RouteCollection 实例。

public class RouteTable
{
  // Fields
  private static RouteCollection _instance = new RouteCollection();
  // Properties
  public static RouteCollection Routes
  {
    get { return _instance; }
  }
}

RouteCollection 除了保存所有路由规则 (Route) 外,还有几个重要的方法。

public class RouteCollection : Collection<RouteBase>
{
  // Methods
  public void Add(string name, RouteBase item);
  public RouteData GetRouteData(HttpContextBase httpContext);
  public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
  public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);
  // Properties
  public RouteBase this[string name] { get; }
  public bool RouteExistingFiles { get; set; }
}

GetRouteData() 通过当前请求上下文,找出最合适的路由规则(Route),并生成当前执行环境所需的动态路由数据(RouteData)。

public class RouteCollection : Collection<RouteBase>
{
  public RouteData GetRouteData(HttpContextBase httpContext)
  {
    // … 省略部分代码 …
    using (this.GetReadLock())
    {
      foreach (RouteBase base2 in this)
      {
        RouteData routeData = base2.GetRouteData(httpContext);
        if (routeData != null)
        {
          return routeData;
        }
      }
    }
    return null;
  }
}

循环调用搜索规则的 GetRouteData 方法来获取动态路由数据,这就是上面我们提到要注意路由规则注册顺序的原因。

public class Route : RouteBase
{
  public override RouteData GetRouteData(HttpContextBase httpContext)
  {
    string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) +
      httpContext.Request.PathInfo;
    RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
    if (values == null)
    {
      return null;
    }
    RouteData data = new RouteData(this, this.RouteHandler);
    if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
    {
      return null;
    }
    foreach (KeyValuePair<string, object> pair in values)
    {
      data.Values.Add(pair.Key, pair.Value);
    }
    if (this.DataTokens != null)
    {
      foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
      {
        data.DataTokens[pair2.Key] = pair2.Value;
      }
    
      return data;
    }
  }
}

Route.GetRouteData() 首先调用 ParsedRoute.Match() 来检查路径是否匹配,接下来使用 ProcessConstraints() 检查相应的约束规则,如果这些检查都得以通过,则生成最终的动态路由数据。RouteCollection.GetRouteData() 在获取该结果后终止循环。
IRouteHandler 的作用是返回一个特定 IHttpHandler,比如 MvcHandler,当然也可以是 "System.Web.UI.Page : IHttpHandler"。

public interface IRouteHandler
{
  IHttpHandler GetHttpHandler(RequestContext requestContext);
}
public class MvcRouteHandler : IRouteHandler
{
  protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    return new MvcHandler(requestContext);
  }
  IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
  {
    return this.GetHttpHandler(requestContext);
  }
}

UrlRoutingModule 的执行顺序,可参考《ASP.NET MVC Preview 2 – 流程分析 (1)》
Route 的相关语法规则,可参考《ASP.NET MVC Preview 3 Release 》(ScottGu 中文版)。

[MVC]ASP.NET MVC Preview 3 流程分析 - 3.View

mikel阅读(915)

P3 改用一堆继承自 ActionResult 的对象来完成视图显示,这是该版本的最大变化。
1. ViewResult
当 我们在 Action Method 中调用 View 生成 ViewResult 的时候,应该注意两个细节,那就是生成默认的 ViewData 字典和 WebFormViewEngine 视图显示引擎。上一节的分析中我们已经知道 ActionFilter.OnActionExecuting 在 Action Method 之前执行,那么我们完全可以定义一个自己的视图引擎,比如用 NVelocity 来代替 WebForm。(可参考《ASP.NET MVC Preview 2 – 流程分析 (3) 》《ASP.NET MVC Preview 2 – NVelocityViewEngine 》)

public abstract class Controller : IActionFilter, IController, IDisposable
{
  public ViewDataDictionary ViewData
  {
    get
    {
      if (_viewData == null)
      {
        _viewData = new ViewDataDictionary();
      }
      return _viewData;
    }
  }
  public IViewEngine ViewEngine
  {
    get
    {
      if (_viewEngine == null)
      {
        _viewEngine = new WebFormViewEngine();
      }
      return _viewEngine;
    }
    set
    {
      // … 省略部分代码 …
      _viewEngine = value;
    }
  }
  protected internal virtual ViewResult View(string viewName, string masterName, object model)
  {
    if (model != null)
    {
      ViewData.Model = model;
    }
    return new ViewResult()
    {
      ViewName = viewName,
      MasterName = masterName,
      ViewData = ViewData,
      ViewEngine = ViewEngine,
      TempData = TempData
    };
  }
}

在 Controller.InvokeAction() 的最后会间接通过 InvokeActionResult() 来调用 ViewResult.ExecuteResult,从而实现对视图引擎的触发 (可参考上一章)。

public class ViewResult : ActionResult
{
  public override void ExecuteResult(ControllerContext context)
  {
    // … 省略部分代码 …
    string viewName = (!String.IsNullOrEmpty(ViewName)) ?
      ViewName : context.RouteData.GetRequiredString("action");
    ViewContext viewContext = new ViewContext(context, viewName, MasterName, ViewData, TempData);
    ViewEngine.RenderView(viewContext);
  }
}

创建视图上下文,并调用 WebFormViewEngine.RenderView()。

public class WebFormViewEngine : IViewEngine
{
  protected virtual void RenderView(ViewContext viewContext)
  {
    // … 省略部分代码 …
    string viewPath = ViewLocator.GetViewLocation(viewContext, viewContext.ViewName);
    // … 省略部分代码 …
    object viewInstance = BuildManager.CreateInstanceFromVirtualPath(viewPath, typeof(object));
    // … 省略部分代码 …
    ViewPage viewPage = viewInstance as ViewPage;
    if (viewPage != null)
    {
      if (!String.IsNullOrEmpty(viewContext.MasterName))
      {
        string masterLocation = ViewLocator.GetMasterLocation(viewContext, viewContext.MasterName);
        
        // … 省略部分代码 …
        // We don't set the page's MasterPageFile directly since it will get
        // overwritten by a statically-defined value. In ViewPage we wait until
        // the PreInit phase until we set the new value.
        viewPage.MasterLocation = masterLocation;
      }
      viewPage.ViewData = viewContext.ViewData;
      viewPage.RenderView(viewContext);
    }
    else
    {
      ViewUserControl viewUserControl = viewInstance as ViewUserControl;
      if (viewUserControl != null)
      {
        // … 省略部分代码 …
        viewUserControl.ViewData = viewContext.ViewData;
        viewUserControl.RenderView(viewContext);
      }
      else
      {
        // … 省略部分代码 …
      }
    }
  }
}

这部分的变化并不大,有关细节可参考 《ASP.NET MVC Preview 2 – 流程分析 (3)》,这里就不在啰嗦了。
2. RedirectResult / RedirectToRouteResult
其实很简单,重建 RouteValueDictionary,然后在 RedirectToRouteResult.ExecuteResult() 中使用 HttpContext.Response.Redirect() 进行跳转。

public abstract class Controller : IActionFilter, IController, IDisposable
{
  protected internal virtual RedirectToRouteResult RedirectToAction(actionName, controllerName, values)
  {
    // … 省略部分代码 …
    RouteValueDictionary newDict = (values != null) ?
      new RouteValueDictionary(values) : new RouteValueDictionary();
    newDict["action"] = actionName;
    if (!String.IsNullOrEmpty(controllerName))
    {
      newDict["controller"] = controllerName;
    }
    return new RedirectToRouteResult(newDict);
  }
  protected internal virtual RedirectToRouteResult RedirectToRoute(routeName, values)
  {
    RouteValueDictionary newDict = (values != null) ?
      new RouteValueDictionary(values) : new RouteValueDictionary();
    return new RedirectToRouteResult(routeName, newDict);
  }
}
public class RedirectToRouteResult : ActionResult
{
  public RedirectToRouteResult(string routeName, RouteValueDictionary values)
  {
    RouteName = routeName ?? String.Empty;
    Values = values ?? new RouteValueDictionary();
  }
  public override void ExecuteResult(ControllerContext context)
  {
    // … 省略部分代码 …
    VirtualPathData vpd = Routes.GetVirtualPath(context, RouteName, Values);
    // … 省略部分代码 …
    string target = vpd.VirtualPath;
    context.HttpContext.Response.Redirect(target);
  }
}

尽管这种跳转会导致 Controller 实例被重新生成,但我们依然可以使用 TempData 传递相关的数据给下一个 Action。(有关 TempData 传递数据的原理,可阅读《ASP.NET MVC Preview 2 – RedirectToAction》)
下面是一个跳转的演示

public class HomeController : Controller
{
  public ActionResult Index()
  {
    TempData["s"] = "Haha…";
    //return RedirectToRoute(new { controller = "Test", action = "Test" });
    return RedirectToAction("About");
  }
  public ActionResult About()
  {
    ViewData["Title"] = TempData["s"].ToString();
    return View();
  }
}
public class TestController : Controller
{
  public ActionResult Test()
  {
    return Content(TempData["s"].ToString());
  }
}

在使用 Controller.RedirectToRoute 时有个参数叫 routeName,其实它就是 Global.asax.cs 中调用 MapRoute 时的定义。

routes.MapRoute(
  "Default",  // Route name
  "{controller}/{action}/{id}",  // URL with parameters
  new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);

3. JsonResult
有了这个,写 Ajax 操作就简单多了。

public class HomeController : Controller
{
  protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding)
  {
    return new JsonResult
    {
      Data = data,
      ContentType = contentType,
      ContentEncoding = contentEncoding
    };
  }
}
public class JsonResult : ActionResult
{
  public override void ExecuteResult(ControllerContext context)
  {
    // … 省略部分代码 …
    HttpResponseBase response = context.HttpContext.Response;
    if (!String.IsNullOrEmpty(ContentType))
    {
      response.ContentType = ContentType;
    }
    else
    {
      response.ContentType = "application/json";
    }
    if (ContentEncoding != null)
    {
      response.ContentEncoding = ContentEncoding;
    }
    if (Data != null)
    {
      JavaScriptSerializer serializer = new JavaScriptSerializer();
      response.Write(serializer.Serialize(Data));
    }
  }
}

写个例子看看。

public class HomeController : Controller
{
  public ActionResult Index()
  {
    return Json(new { Name = "Rose", Sex = "Male", Age = 31 });
  }
}

看看返回的 Http 结果。

HTTP/1.1 200 OK
Server: ASP.NET Development Server/9.0.0.0
Date: Sat, 14 Jun 2008 11:24:46 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: application/json; charset=utf-8
Content-Length: 37
Connection: Close
{"Name":"Rose","Sex":"Male","Age":31}

4. ContentResult
这个纯粹就是代码缩写了,将 "Response.Write(…)" 简写成 "Content(…)"。 [lol]

public class ContentResult : ActionResult
{
  public override void ExecuteResult(ControllerContext context)
  {
    // … 省略部分代码 …
    HttpResponseBase response = context.HttpContext.Response;
    if (!String.IsNullOrEmpty(ContentType))
    {
      response.ContentType = ContentType;
    }
    if (ContentEncoding != null)
    {
      response.ContentEncoding = ContentEncoding;
    }
    if (Content != null)
    {
      response.Write(Content);
    }
  }
}

5. EmptyResult
这个最好,没啥可说,没啥可做。

public class EmptyResult : ActionResult
{
  public override void ExecuteResult(ControllerContext context)
  {
  }
}

唯一要关注的是,ControllerActionInvoker.InvokeActionMethod 会将 Action return null 转为为 EmptyResult。

public class ControllerActionInvoker
{
  protected virtual ActionResult InvokeActionMethod(methodInfo, parameters)
  {
    // … 省略部分代码 …
    object returnValue = methodInfo.Invoke(controller, parametersArray);
    if (returnValue == null)
    {
      return new EmptyResult();
    }
    
    // … 省略部分代码 …
  }
}

[MVC]ASP.NET MVC Preview 3 流程分析 - 2.Controller

mikel阅读(926)

接着上一章留下的线索,我们开始分析 Controller 的执行过程。
1. Controller.Execute

public abstract class Controller : IActionFilter, IController, IDisposable
{
  protected internal virtual void Execute(ControllerContext controllerContext)
  {
    // … 省略部分代码 …
    ControllerContext = controllerContext;
    TempData = new TempDataDictionary(controllerContext.HttpContext);
    string actionName = RouteData.GetRequiredString("action");
    ControllerActionInvoker invoker = ActionInvoker ?? new ControllerActionInvoker(controllerContext);
    
    if (!invoker.InvokeAction(actionName, new Dictionary<string, object>()))
    {
      HandleUnknownAction(actionName);
    }
  }
}

熟 悉的面孔 —— TempData。和 ViewData 的目标不同,TempData 主要用于 Controller 内部的数据传递。从 Router 字典中取出 Action 的名字,并创建一个专门的 Invoker 来完成整个执行工作,包括过滤器(ActionFilterAttribute)、Action Method,以及视图显示(IViewDataContainer)。MVC 给了我们一个选择,我们可以继承并创建一个自定义的 Invoker 去改变一些内在的规则 (在 Controller.ctor 中对 ActionInvoker 赋值)。
2. ControllerActionInvoker.InvokeAction
如 果你看书很仔细的话,难道你对上面 Execute 方法里面的 "invoker.InvokeAction(actionName, new Dictionary<string, object>())" 语句不感到奇怪吗?一个没有任何引用的空 Dictionary,这似乎不合乎情理。传递参数?返回某些值?注意看下面的分析。

public class ControllerActionInvoker
{
  public virtual bool InvokeAction(string actionName, IDictionary<string, object> values)
  {
    // … 省略部分代码 …
    MethodInfo methodInfo = FindActionMethod(actionName, values);
    if (methodInfo != null)
    {
      IDictionary<string, object> parameters = GetParameterValues(methodInfo, values);
      IList<IActionFilter> filters = GetAllActionFilters(methodInfo);
      ActionExecutedContext postContext = InvokeActionMethodWithFilters(methodInfo, parameters, filters);
      InvokeActionResultWithFilters(postContext.Result, filters);
      // notify controller of completion
      return true;
    }
    // notify controller that no method matched
    return false;
  }

FindActionMethod 方法利用反射返回 Action 方法的信息,注意 NonActionAttribute、Controller_ActionCannotBeGeneric。很显然这个作者有非常好的编码习惯,代码注 释非常清晰有用,值得学习。可以有和 Action 签名相同的重载方法,但必须加上 NonActionAttribute 特性。同时 Action 不能是泛型方法。

protected virtual MethodInfo FindActionMethod(string actionName, IDictionary<string, object> values)
{
  // … 省略部分代码 …
  // We have to loop through all the methods to make sure there isn't
  // a conflict. If we stop the loop the first time we find a match
  // we might miss some error cases.
  MemberInfo[] memberInfos = ControllerContext.Controller.GetType().GetMember(
    actionName, MemberTypes.Method,
    BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
  MethodInfo foundMatch = null;
  foreach (MethodInfo methodInfo in memberInfos)
  {
    // (1) Action methods must not have the non-action attribute in their inheritance chain,
    // and (2) special methods like constructors, property accessors, and event accessors cannot
    // be action methods, and (3) methods originally defined on Object (like ToString()) or
    // Controller (like Dispose()) cannot be action methods.
    if (!methodInfo.IsDefined(typeof(NonActionAttribute), true) &&
      !methodInfo.IsSpecialName &&
      !methodInfo.GetBaseDefinition().DeclaringType.IsAssignableFrom(typeof(Controller)))
    {
      if (foundMatch != null)
      {
        throw new InvalidOperationException(String.Format(
          CultureInfo.CurrentUICulture, MvcResources.Controller_MoreThanOneAction,
          actionName, ControllerContext.Controller.GetType()));
      }
      
      foundMatch = methodInfo;
    }
  }
  if (foundMatch != null)
  {
    if (foundMatch.ContainsGenericParameters)
    {
      throw new InvalidOperationException(String.Format(
        CultureInfo.CurrentUICulture, MvcResources.Controller_ActionCannotBeGeneric,
        foundMatch.Name));
    }
  }
  return foundMatch;
}

GetParameterValues 方法的作用是从环境上下文中获取 Action 方法执行所需的参数 (Argument)。比如 Action 的方法签名是 "public ActionResult Test(int x, int y)",那么 GetParameterValues 则必须返回一个类似 "{{x, 123}, {y, 456 }}" 这样的参数字典,在反射调用 Test 时,传递过去。

protected virtual IDictionary<string, object> GetParameterValues(methodInfo, values)
{
  // … 省略部分代码 …
  var parameterDict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
  foreach (ParameterInfo parameterInfo in methodInfo.GetParameters())
  {
    if (parameterInfo.IsOut || parameterInfo.ParameterType.IsByRef)
    {
      throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
        MvcResources.Controller_ReferenceParametersNotSupported,
        parameterInfo.Name,
        'methodInfo.Name));
    }
    parameterDict[parameterInfo.Name] = GetParameterValue(parameterInfo, values);
  }
  return parameterDict;
}

循环提取所有的 ParameterInfo,不过这里有了另外一个限制,那就是方法参数不能使用 ref / out 关键字。GetParameterValue 方法应该是获取具体参数值,一路传递过来的那个空字典被丢了进去。

protected virtual object GetParameterValue(parameterInfo, values)
{
  // … 省略部分代码 …
  Type parameterType = parameterInfo.ParameterType;
  string parameterName = parameterInfo.Name;
  string actionName = parameterInfo.Member.Name;
  bool valueRequired = !TypeHelpers.TypeAllowsNullValue(parameterType);
  // Try to get a value for the parameter. We use this order of precedence:
  // 1. Explicitly-provided extra parameters in the call to InvokeAction()
  // 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
  // 3. Request values (query string, form post data, cookie)
  object parameterValue = null;
  if (!(values != null && values.TryGetValue(parameterName, out parameterValue)))
  {
    if (!(ControllerContext.RouteData != null &&
      ControllerContext.RouteData.Values.TryGetValue(parameterName, out parameterValue)))
    {
      if (ControllerContext.HttpContext != null && ControllerContext.HttpContext.Request != null)
      {
        parameterValue = ControllerContext.HttpContext.Request[parameterName];
      }
    }
  }
  
  // … 省略部分代码 …
  try
  {
    return ConvertParameterType(parameterValue, parameterType, parameterName, actionName);
  }
  catch (Exception ex)
  {
    // … 省略部分代码 …
  }
  return null;
}

再次夸一下作者,那几行注释说明了一切。对于 Action 方法参数的获取,有三种优先级不同的方式:
1. 从调用 InvokeAction 传递进来的参数字典中提取。(就是那个莫名其妙的字典,这意味着自定义 Invoker 有机会对请求参数做出调整)
2. 从 RouteData 中查找,这有两种可能:第一是 URL 请求中已有的参数;其次就是 routes.MapRoute 定义的缺省值。
3. 如果上述两个方法都没有找到参数值,那只好从 Request 中提取了。(某些时候,MapRoute 的定义并不完整,尤其是 {Controller}/{Action} 这种通用规则)
在准备好 Action 的相关数据后,InvokeAction 调用 GetAllActionFilters 获取所有的过滤器。

protected virtual IList<IActionFilter> GetAllActionFilters(MethodInfo methodInfo)
{
  // … 省略部分代码 …
  // use a stack since we're building the member chain backward
  Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
  // first, push the most derived action method, then its base method, and so forth
  memberChain.Push(methodInfo);
  
  MethodInfo baseMethod = methodInfo.GetBaseDefinition();
  Type curType = methodInfo.DeclaringType.BaseType;
  while (true)
  {
    MemberInfo[] memberInfos = curType.GetMember(methodInfo.Name, MemberTypes.Method,
      BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
    MethodInfo foundMatch = null;
    foreach (MethodInfo possibleMatch in memberInfos)
    {
      if (possibleMatch.GetBaseDefinition() == baseMethod)
      {
        foundMatch = possibleMatch;
        break;
      }
    }
    if (foundMatch == null)
    {
      // we've passed the declaring type of the base method
      break;
    }
    if (foundMatch.DeclaringType == curType)
    {
      // only push if there's no jump in the inheritance chain
      memberChain.Push(foundMatch);
    }
    curType = curType.BaseType;
  }
  // second, push the current controller type, then its base type, and so forth
  curType = ControllerContext.Controller.GetType();
  while (curType != null)
  {
    memberChain.Push(curType);
    curType = curType.BaseType;
  }
  // now build the actual filter list up from the beginning. add the current controller
  // if it implements IActionFilter, then process the memberInfo stack.
  List<IActionFilter> filterList = new List<IActionFilter>();
  IActionFilter controllerFilter = ControllerContext.Controller as IActionFilter;
  if (controllerFilter != null)
  {
    filterList.Add(controllerFilter);
  }
  foreach (MemberInfo memberInfo in memberChain)
  {
    filterList.AddRange(GetActionFiltersForMember(memberInfo));
  }
  return filterList;
}

这个方法看上去有点复杂。
1. 创建一个后进先出的栈。
2. 将 Action 压进去。
3. 循环向上查找所有级别的 Base Type,并将 Base Action Method 压到栈里。
4. 接下来将当前 Controller Type 以及其所有 Base Type 先后压到栈里。
5. 创建一个列表用于存储实际的 IActionFilter 集合。
6. 将当前 Controller 添加到列表,别忘了默认情况下控制器本身也实现了 IActionFilter。
7. 使用循环依次从栈中弹出 Base Type、Base Action Method 以及 Current Action Method,并调用 GetActionFiltersForMember 获取其所有 ActionFilterAttribute 定义。
8. 返回一个过滤器列表。(由于是后进先出的栈,因此列表内部顺序是:Current Controller、Base Type Filter、Base Action Method Filter、Current Action Method Filter)
注意 ActionFilterAttribute 是可以应用的 Class 上的,也就是说对所有的 Action 方法都有效。现在你该理解上面获取基类型的原因了吧。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, …)]
public abstract class ActionFilterAttribute : Attribute, IActionFilter { … }

当然,我们还是要看看 GetActionFiltersForMember。

protected virtual IList<IActionFilter> GetActionFiltersForMember(MemberInfo memberInfo)
{
  // … 省略部分代码 …
  List<IActionFilter> unorderedFilters = new List<IActionFilter>();
  SortedList<int, IActionFilter> orderedFilters = new SortedList<int, IActionFilter>();
  ActionFilterAttribute[] attrs = (ActionFilterAttribute[])memberInfo.GetCustomAttributes(
    typeof(ActionFilterAttribute), false /* inherit */);
  foreach (ActionFilterAttribute filter in attrs)
  {
    // filters are allowed to have the same order only if the order is -1. in that case,
    // they are processed before explicitly ordered filters but in no particular order in
    // relation to one another.
    if (filter.Order >= 0)
    {
      if (orderedFilters.ContainsKey(filter.Order))
      {
        MethodBase methodInfo = memberInfo as MethodBase;
        // … 省略抛出异常代码 …
      }
      orderedFilters.Add(filter.Order, filter);
    }
    else
    {
      unorderedFilters.Add(filter);
    }
  }
  // now append the ordered list to the unordered list to create the final list
  unorderedFilters.AddRange(orderedFilters.Values);
  return unorderedFilters;
}

这 个方法很简单,用反射查找 ActinFilterAttribute。唯一需要注意的是 order 这个排序属性,返回的列表是按此属性排序过的。ActionFilterAttribute.Order 默认等于 -1,也就说不修改这个排序属性的话,会按照其定义顺序返回。另外还有一个需要注意的地方,如果 ActionFilterAttribute.Order > 0,那么多个 Filter.Order 不能相同,否则会抛出类似下面这样的异常。

The action method 'Test' on controller 'Learn.MVC.Controllers.HomeController' has two filter attributes with filter order 1. If a filter specifies an order of 0 or greater, no other filter on that action method may specify that same order.

回到正题,InvokeAction 获得全部 Filter 后,InvokeActionMethodWithFilters 被调用。

protected virtual ActionExecutedContext InvokeActionMethodWithFilters(methodInfo, parameters, filters)
{
  // … 省略部分代码 …
  ActionExecutingContext preContext = new ActionExecutingContext(ControllerContext,
    methodInfo, parameters);
  
  Func<ActionExecutedContext> continuation = () =>
    new ActionExecutedContext(ControllerContext, methodInfo, null /* exception */)
    {
      Result = InvokeActionMethod(methodInfo, 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();
}

将 Action Method 用委托进行包装,反转过滤器列表进行集合累计调用。Aggregate + InvokeActionMethodFilter 通过递归调用完成 Filter OnActionExecuting -> Current Action Execute -> Filter OnActionExecuted 这样一个执行过程。下面是调用结果示例。

CurrentController.OnActionExecuting
BaseClassFilter.OnActionExecuting
CurrentClassFilter.OnActionExecuting
BaseActionFilter.OnActionExecuting
CurrentActionFilter.OnActionExecuting
InvokeActionMethod -> Action
CurrentActionFilter.OnActionExecuted
BaseActionFilter.OnActionExecuted
CurrentClassFilter.OnActionExecuted
BaseClassFilter.OnActionExecuted
CurrentController.OnActionExecuted

InvokeAction 紧接着会通过调用 InvokeActionResultWithFilters 完成对 Filter.OnResultExecuting 和 OnResultExecuted 的调用,这是 P2 所没有的。
InvokeActionResultWithFilters 和 InvokeActionMethodWithFilters 过程差不多,都是递归调用。需要特别注意的是 InvokeActionResult 代替了 InvokeActionMethod。(注意:尽管 BaseController 本身也是一个 ActionFilterAttribute,但并不会被调用)

protected virtual ResultExecutedContext InvokeActionResultWithFilters(ActionResult actionResult, IList<IActionFilter> filters)
{
  // … 省略部分代码 …
  ResultExecutingContext preContext = new ResultExecutingContext(ControllerContext, actionResult);
  Func<ResultExecutedContext> continuation = delegate
  {
    InvokeActionResult(actionResult);
    return new ResultExecutedContext(ControllerContext, preContext.Result, null /* exception */);
  };
  // need to reverse the filter list because the continuations are built up backward
  Func<ResultExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
    (next, filter) => () => InvokeActionResultFilter(filter, preContext, next));
  return thunk();
}

执行结果顺序演示:

CurrentController.OnResultExecuting
BaseClassFilter.OnResultExecuting
CurrentClassFilter.OnResultExecuting
BaseActionFilter.OnResultExecuting
CurrentActionFilter.OnResultExecuting
InvokeActionResult -> ActionResult.ExecuteResult() -> IViewEngine.RenderView()
CurrentActionFilter.OnResultExecuted
BaseActionFilter.OnResultExecuted
CurrentClassFilter.OnResultExecuted
BaseClassFilter.OnResultExecuted
CurrentController.OnResultExecuted

我们看看 InvokeActionResult 做了些什么。

protected virtual void InvokeActionResult(ActionResult actionResult)
{
  // … 省略部分代码 …
  actionResult.ExecuteResult(ControllerContext);
}

调 用 ActionResult.ExecuteResult 方法?有什么作用呢?如果你已经看过 P3 的帮助文件的话,你应该知道 Action Method 可以通过 Controller.View()、Controller.Json()、Controller.RedirectToRouteResult() 方法返回如下几种 ActionResult。

public class ViewResult : ActionResult {}
public class JsonResult : ActionResult {}
public class RedirectToRouteResult : ActionResult {}

先不管后面两种,看看 ViewResult.ExecuteResult() 会做什么?

public class ViewResult : ActionResult
{
  public override void ExecuteResult(ControllerContext context)
  {
    // … 省略部分代码 …
    string viewName = (!String.IsNullOrEmpty(ViewName)) ?
      ViewName : context.RouteData.GetRequiredString("action");
    ViewContext viewContext = new ViewContext(context, viewName, MasterName, ViewData, TempData);
    ViewEngine.RenderView(viewContext);
  }

HoHo~~~~ 终于看到了视图显示的曙光了,不过这属于下一篇的内容。
———————–
ControllerActionInvoker.InvokeAction()、Controller.Execute() 执行至此结束。
执行流程图

uploads/200806/13_000050_small.png

查看大图

[C#]System.Web.Routing入门及进阶

mikel阅读(1105)

System.Web.Routing已经作为一个程序集包含在.net3.5sp1中发布了。虽然我们并没有在3.5sp1中发现ASP.NET Mvc的踪迹,但是亦以感觉到它离我们不远了。

System.Web.Routing用于在ASP.NETWeb应用程序中进行URLRouting

 

所谓UrlRouting就是将一个地址映射为另一个地址,比如我访问/chsword/2008/08/27.html其实是访问了 /chsword/article.aspx?y=2008&m=08&d=27这个地址,URLRouting使我们的URL看起来非 常漂亮。

 

本文将分2部分介绍UrlRouting的使用分别为入门篇与进阶篇。

文章的前提:

1.         安装Visual Studio 2008 sp1或其它IDE的等同版本。

2.         建立一个Asp.net Web ApplicationAsp.net Web应用程序)

3.         引用System.Web.Routing程序集。

UrlRouting的实现原理

如果你不是追求理论的人,仅仅是务实主义,可以直接从准备工作读起。

为了方便大家理解我绘制了一个UML图,其中通过RouteTableRoutes这个属性可以获取一个RouteCollectionSingleton模式,虽然在其中并没有判断值不存在时才初始化的Singleton的标志性行为,但是它是在Application_Start事件中进行初始化的,并且直到应用程序进程终结,所以是Singleton模式的。

 

而通过以下方式将Route添加到RouteTable.Routes

RouteTable.Routes.Add(new Route());

以上代码仅为表示其流程,这个代码是不能正确执行的,因为Route没有提供无参构造函数。

Route初始化时则是利用RouteValueDictionary来加入默认值及规则到Route

另外Route还有一个IRouteHandler的实现对象,IRouteHandler的实现对象提供了一个GetHttpHandler方法来获取用于处理URLIHttpHandler

 

这么说还是停留在抽象层次的,下面我们以一些简单例子来带你驭起UrlRouting

准备工作

由于须要一个处理UrlIHttpHandler所以我们先定义一个实现了IHttpHandler接口的类。

就叫它MyPage,由于我们要与IRouteHandler交互,所以除了实现IHttpHandler的方法之外还要声明一个RequestContext类型的属性

public class MyPage:IHttpHandler {
        
public RequestContext RequestContext { getprivate set; }
        
public MyPage(RequestContext context)
        {
            
this.RequestContext = context;
        }
        
#region IHttpHandler 成员
        
public virtual void ProcessRequest(HttpContext context){}
        
public bool IsReusable {
            
get { return false; }
        }
        
#endregion
    }

这样我们就拥有了一个自己的IHttpHandler

下面我们实现一个IRouteHandler

    public class MyRouteHandler : IRouteHandler {
        
#region IRouteHandler 成员
        
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
            
return new MyPage(requestContext);
        }
        
#endregion
    }

这里实现了IRouteHandlerGetHttpHandler方法,使之返回刚才我们实现的MyPage

这样我们前期的2个工作就做完了,我们可以来实现UrlRouting了。

实现第一个UrlRouting

其实UrlRouting在定义完上两个规则后就很简单了。

Golbal.asax(没有可以新建一个)的Application_Start事件中写如下代码

 

protected void Application_Start(object sender, EventArgs e) {
            RegisterRoutes(RouteTable.Routes);
        }
        
public static void RegisterRoutes(RouteCollection routes) {
            routes.Add(
new Route("{page}.aspx",new MyRouteHandler()));
        }

这样我们就定义了第一个UrlRouting规则就是对xxxx.aspx这类的Url进行Routing

 

但是我们仅仅是定义了处理了什么样的Url,却没定义如何处理。

我们应该在刚刚定义的MyPageProcessRequest方法中定义如何处理。

我们将ProcessRequest方法实现如下:

 

public virtual void ProcessRequest(HttpContext context){
context.Server.Execute(        RequestContext.RouteData.Values[
"page"].ToString().Replace("_","/")+".aspx"
            );
}

很显然这里的RequestContext.RouteData.Values["page"]就是取到刚才的规则{page}.aspx中的page的值即,如果我访问index.aspxRequestContext.RouteData.Values["page"]就是index

我这里的定义是将”_”替换为”/”所以就是将list_index.aspx这样的URL转到list/index.aspx这样的网页上。

我们建立一些测试用的网页如下图所示:

 

 

在这些网页里随意写些可以分辨网页的文字。

则访问list_chsword.aspx时自动Routing到了list/chsword.aspx上了

总结一下UrlRouting与以下有关:

1.         Application_Start中定义的规则

2.         自己实现的IHttpHandler

 

上面介绍的是最简单的一种定义方式。当然我们可以建立更复杂的规则。其中就包括设定规则的默认值以及设定规则的正则表达式。

UrlRouting高级应用

预计效果:

当我访问/a/b.aspx时就会转到Default.aspx?category=a&action=b在页面上显示
category:a
action:b
亦如果我访问/chsword/xxxx.aspx就会转到Default.aspx?category
=chsword&action=xxxx就会显示
category:chsword

action:xxxx

 

如果访问/chsword/就会转到 Default.aspx?category=chsword&action=index就会显示

 category:chsword

action:index

 

 首先我建立一个Route

routes.Add(
                
"Default",
                
new Route("{category}/{action}.aspx",
                          
new RouteValueDictionary(
                              
new
                                  {
                                      file 
= "Default",
                                      category 
= "home",
                                      action 
= "index"
                                  }), 
new MyRouteHandler()
                    )
                );

当然IHttpHandler的处理方式也要有所改变

为了方便查看我使用了下方法:

 

    context.Server.Execute(string.Format("/{0}.aspx?category={1}&action={2}",
              RequestContext.RouteData.Values.ContainsKey(
"file")
                
? RequestContext.RouteData.Values["file"].ToString()
                : 
"default",
              RequestContext.RouteData.Values.ContainsKey(
"category")
                
? RequestContext.RouteData.Values["category"].ToString()
                : 
"",
              RequestContext.RouteData.Values.ContainsKey(
"action")
                
? RequestContext.RouteData.Values["action"].ToString()
                : 
"")
                );

 

即/a/b.aspx是映射到Default.aspx?category=a&action=b

在Default.aspx中写如下代码:

 category:<%=Request.Params["category"%><br />
   action:
<%=Request.Params["action"%>

 

以显示传入的参数。

如果在IIS中设置Index.aspx时就算输入/a/也会访问到/a/index.aspx,即默认的会按RouteValueDictionary中设置的值自动补全

UrlRouting使用正则表达式规则

UrlRouting在定义的时候也可以按正则的规则来进行定义。

 

 

            routes.Add(
                
"zz",
                
new Route("{category}/{action}.chs",
                    
new RouteValueDictionary(
                        
new {
                            file 
= "Default",
                            category 
= "home",
                            action 
= "1"
                        }),
                        
new RouteValueDictionary(
                        
new {
                            action 
= "[\\d]+"
                        }),
                    
new MyRouteHandler()
                    )
            );

 

以上代码规定了action只能是数字则访问/a/1.chs可以正常访问。

而访问/a/b.chs则会显示未找到资源。

 

当然这是里可以使用更高级的正则表达式。

UrlRouting的技巧

排除UrlRouting的方法:

System.Web.Routing默认提供了一个IRouteHandlerStopRoutingHandler Class,经过它处理的URL不会被做任何处理

通常使用方法如下:

routes.Add(new Route("{resource}.axd/{*pathInfo}", new StopRoutingHandler()));

 

RouteHandler工厂:

其实IRouteHandler可以实现一个RouteHandler的简单工厂。

    public class RouteHandlerFactory : IRouteHandler
    {
        
string Name { getset; }
        
public RouteHandlerFactory(string name){this.Name = name;}
        
#region IRouteHandler 成员
        
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
            
if (this.Name == "mypage")
                
return new MyPage(requestContext);
            
if(this.Name="mypage1")
                
return new MyPage1(requestContext);
        }
        
#endregion
    }

 

 规定HTTP verbs,这里要使用System.Web.Routing中的HttpMethodConstraint

 

void Application_Start(object sender, EventArgs e) {
    RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes){
    
string[] allowedMethods = { "GET""POST" };
    HttpMethodConstraint methodConstraints 
= new HttpMethodConstraint(allowedMethods);
    Route reportRoute 
= new Route("{locale}/{year}"new ReportRouteHandler());
    reportRoute.Constraints 
= new RouteValueDictionary { { "httpMethod", methodConstraints } };
    routes.Add(reportRoute);
}

 

Demo程序代码下载:

 http://files.cnblogs.com/chsword/WebApplication3.rar

[MVC]ASP.NET MVC Preview 3 流程分析 - 1. MvcHandler

mikel阅读(967)

1. 这次的旅程将从 MvcRouteHandler 开始。(有源码,分析起来就是方便~~~~ 调整一下,直接在源码设断点)

System.Web.Mvc.DLL!System.Web.Mvc.MvcRouteHandler.System.Web.Routing.IRouteHandler.GetHttpHandler(…
System.Web.Routing.DLL!System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(…
System.Web.Routing.DLL!System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(…
System.Web.dll!System.Web.HttpApplication…..System.Web.HttpApplication.IExecutionStep.Execute(…
System.Web.dll!System.Web.HttpApplication.ExecuteStep(System.Web.HttpApplication.IExecutionStep step…
System.Web.dll!System.Web.HttpApplication.ApplicationStepManager.ResumeSteps(…
System.Web.dll!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(…
System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest wr…
System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand(…
System.Web.dll!System.Web.HttpRuntime.ProcessRequest(…


上面是起始调用堆栈,我们会发现 UrlRoutingModule 通过调用 MvcRouteHandler.GetHttpHandler() 引发本次追逃行动。

public class MvcRouteHandler : IRouteHandler
{
  protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    return new MvcHandler(requestContext);
  }


生成一个 MvcHandler 对象,同时将请求上下文传递过去。接下来的事情自然交给这个 IHttpHandler 了。
2. MvcHandler.ProcessRequest 被系统调用。

public class MvcHandler : IHttpHandler, IRequiresSessionState
{
  void IHttpHandler.ProcessRequest(HttpContext httpContext)
  {
    ProcessRequest(httpContext);
  }
  protected virtual void ProcessRequest(HttpContext httpContext)
  {
    HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
    ProcessRequest(iHttpContext);
  }


创建一个新的上下文包装器 —— HttpContextWrapper,这里面有大量的属性和方法方便我们获取相关环境及请求上下文信息。

public class HttpContextWrapper : HttpContextBase
{
  public HttpContextWrapper(HttpContext httpContext);
  public override Exception[] AllErrors { get; }
  public override HttpApplicationStateBase Application { get; }
  public override HttpApplication ApplicationInstance { get; set; }
  public override Cache Cache { get; }
  public override IHttpHandler CurrentHandler { get; }
  public override RequestNotification CurrentNotification { get; }
  public override Exception Error { get; }
  public override IHttpHandler Handler { get; set; }
  public override bool IsCustomErrorEnabled { get; }
  public override bool IsDebuggingEnabled { get; }
  public override bool IsPostNotification { get; }
  public override IDictionary Items { get; }
  public override IHttpHandler PreviousHandler { get; }
  public override ProfileBase Profile { get; }
  public override HttpRequestBase Request { get; }
  public override HttpResponseBase Response { get; }
  public override HttpServerUtilityBase Server { get; }
  public override HttpSessionStateBase Session { get; }
  public override bool SkipAuthorization { get; set; }
  public override DateTime Timestamp { get; }
  public override TraceContext Trace { get; }
  public override IPrincipal User { get; set; }
  public override void AddError(Exception errorInfo);
  public override void ClearError();
  // … 省略部分代码 …
}


回到 MvcHandler 的执行流程。

protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
  // Get the controller type
  string controllerName = RequestContext.RouteData.GetRequiredString("controller");
  // Instantiate the controller and call Execute
  IControllerFactory factory = ControllerBuilder.GetControllerFactory();
  IController controller = factory.CreateController(RequestContext, controllerName);
  // … 省略部分代码 …
  try
  {
    ControllerContext controllerContext = new ControllerContext(RequestContext, controller);
    controller.Execute(controllerContext);
  }
  finally
  {
    factory.DisposeController(controller);
  }
}


从上下文的 Route 字典里查找 Controller 的类型名称(Type Name),然后交由 ControllerFactory 创建控制器对象实例。控制器的执行(Execute)和清理(DisposeController)我们放到下一篇,现在的任务是看看 ControllerBuilder 都做了些什么?

public class MvcHandler : IHttpHandler, IRequiresSessionState
{
  internal ControllerBuilder ControllerBuilder
  {
    get
    {
      if (_controllerBuilder == null)
      {
        _controllerBuilder = ControllerBuilder.Current;
      }
      return _controllerBuilder;
    }
    set
    {
      _controllerBuilder = value;
    }
  }
}


有个默认实例,进去看看。

public class ControllerBuilder
{
  private static ControllerBuilder _instance = new ControllerBuilder();
  private Func<IControllerFactory> _factoryThunk;
  public ControllerBuilder()
  {
    SetControllerFactory(new DefaultControllerFactory());
 }
  public static ControllerBuilder Current
  {
    get { return _instance; }
  }


很简单,不是吗?在构造方法里面我们看到了 Factory。

public void SetControllerFactory(IControllerFactory controllerFactory)
{
  if (controllerFactory == null)
  {
    throw new ArgumentNullException("controllerFactory");
  }
  _factoryThunk = () => controllerFactory;
}


创建 _factoryThunk 委托,返回构造方法中创建的 DefaultControllerFactory 对象。还有另外一个 SetControllerFactory 重载方法,基本意思差不多,只不过反射 Type 来创建 IControllerFactory 实例而已。
MvcHandler.ProcessRequest() 里面的调用的 GetControllerFactory() 方法又干了些什么呢?

public IControllerFactory GetControllerFactory()
{
  IControllerFactory controllerFactoryInstance = _factoryThunk();
  return controllerFactoryInstance;
}


我晕~~~~ 其实拐来拐去还是返回构造里面那个 DefaultControllerFactory 实例。
接下来 MvcHandler.ProcessRequest() 里面会调用 "factory.CreateController(RequestContext, controllerName)" 来创建控制器。这次去 DefaultControllerFactory 它们家溜达溜达。
内部对控制器相关信息做了缓存以提高系统效率。

public class DefaultControllerFactory : IControllerFactory
{
  protected internal virtual IController CreateController(…)
  {
    // … 省略部分代码 …
    RequestContext = requestContext;
    Type controllerType = GetControllerType(controllerName);
    IController controller = GetControllerInstance(controllerType);
    return controller;
 }
  protected internal virtual Type GetControllerType(string controllerName)
  {
    // … 省略部分代码 …
    // 检查缓存标记,利用反射将未缓存的相关控制器类型添加到缓存
    if (!ControllerTypeCache.Initialized)
    {
      // … 省略部分代码 …
    }
    // Once the master list of controllers has been created we can quickly index into it
    Type controllerType;
    // 用控制器名称从缓存中提取控制器类型,为空则抛出异常。
    if (ControllerTypeCache.TryGetControllerType(controllerName, out controllerType))
    {
      if (controllerType == null)
      {
        // A null value indicates a conflict for this key (i.e. more than one match)
        throw new InvalidOperationException(
          String.Format(
            CultureInfo.CurrentUICulture,
            MvcResources.DefaultControllerFactory_DuplicateControllers,
            controllerName));
      }
      return controllerType;
    }
    return null;
  }
  protected internal virtual IController GetControllerInstance(Type controllerType)
  {
    // … 省略部分代码 …
    try
    {
      return Activator.CreateInstance(controllerType) as IController;
    }
    catch (Exception ex)
    {
      // … 省略部分代码 …
    }
  }


整个过程很简单,利用传递进来的控制器类型名称参数,反射查找相应的类型(Type)信息,然后交由 Activator.CreateInstance 创建控制器对象实例。
流程分析图

 

点击看大图

[C#]C#强化系列文章八:HttpModule,HttpHandler

mikel阅读(949)

这三个对象我们在开发ASP.NET程序时经常会用到,似乎很熟悉,但有时候又不太确定。本文通过一个简单的例子来直观的比较一下这三个对象的使用。
HttpModule:Http模块,可以在页面处理前后、应用程序初始化、出错等时候加入自己的事件处理程序
HttpHandler:Http处理程序,处理页面请求
HttpHandlerFactory:用来创建Http处理程序,创建的同时可以附加自己的事件处理程序

例子很简单,就是在每个页面的头部加入一个版权声明。
一、HttpModule
这个对象我们经常用来进行统一的权限判断、日志等处理。
例子代码:

    public class MyModule : IHttpModule
    
{
        
public void Init(HttpApplication application)
        
{
            application.BeginRequest 
+= new EventHandler(application_BeginRequest);
        }


        
void application_BeginRequest(object sender, EventArgs e)
        
{
            ((HttpApplication)sender).Response.Write(
"Copyright @Gspring<br/>");
        }


        
public void Dispose()
        
{
        }

    }

web.config中配置:

      <httpModules>
        
<add name="test" type="HttpHandle.MyModule, HttpHandle"/>
      
</httpModules>

在Init方法中可以注册很多application的事件,我们的例子就是在开始请求的时候加入自己的代码,将版权声明加到页面的头部

二、HttpHandler

这个对象经常用来加入特殊的后缀所对应的处理程序,比如可以限制.doc的文件只能给某个权限的人访问。
ASP.NET中的Page类就是一个IHttpHandler的实现
例子代码:

    public class MyHandler : IHttpHandler
    
{
        
public void ProcessRequest(HttpContext ctx)
        
{
            ctx.Response.Write(
"Copyright @Gspring<br/>");
        }

        
public bool IsReusable
        
{
            
get return true; }
        }

    }

web.config中配置:

      <httpHandlers>
      
<add verb="*" path="*.aspx" type="HttpHandle.MyHandler, HttpHandle"/>
      
</httpHandlers>

这个对象主要就是ProcessRequest方法,在这个方法中输出版权信息,但同时也有一个问题:原来的页面不会被处理,也就是说页面中只有版权声明了。那么所有的aspx页面都不能正常运行了

三、HttpHandlerFactory

这个对象也可以用来加入特殊的后缀所对应的处理程序,它的功能比HttpHandler要更加强大,在系统的web.config中就是通过注册HttpHandlerFactory来实现aspx页面的访问的:

      <add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="true"/>

HttpHandlerFactory是HttpHandler的工厂,通过它来生成不同的HttpHandler对象。
例子代码:

    public class MyHandlerFactory : IHttpHandlerFactory
    
{
        
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        
{
            PageHandlerFactory factory 
= (PageHandlerFactory)Activator.CreateInstance(typeof(PageHandlerFactory), true);
            IHttpHandler handler 
= factory.GetHandler(context, requestType, url, pathTranslated);

            
//执行一些其它操作
            Execute(handler);

            
return handler;
        }


        
private void Execute(IHttpHandler handler)
        
{
            
if (handler is Page)
            
{
                
//可以直接对Page对象进行操作
                ((Page)handler).PreLoad += new EventHandler(MyHandlerFactory_PreLoad);
            }

        }


        
void MyHandlerFactory_PreLoad(object sender, EventArgs e)
        
{
            ((Page)sender).Response.Write(
"Copyright @Gspring<br/>");
        }


        
public void ReleaseHandler(IHttpHandler handler)
        
{
        }

    }

web.config中配置:

      <httpHandlers>
      
<add verb="*" path="*.aspx" type="HttpHandle.MyHandlerFactory, HttpHandle"/>
      
</httpHandlers>

在例子中我们通过调用系统默认的PageHandlerFactory类进行常规处理,然后在处理过程中加入自己的代码,可以在Page对象上附加自己的事件处理程序。

附一个小的恶作剧:
我们可以开发好aspx页面,然后把web应用程序发布后把所有的aspx文件的后缀都改为spring,再在web.config中加入配置:

      <httpHandlers>
      
<add verb="*" path="*.spring" type="HttpHandle.MyHandlerFactory, HttpHandle"/>
      
</httpHandlers>
      
<compilation>
        
<buildProviders>
          
<add extension=".spring" type="System.Web.Compilation.PageBuildProvider"/>
        
</buildProviders>
      
</compilation>

buildProviders是用来指定spring后缀的编译程序,我们把它设置成和aspx一致就可以了。如果在IIS中发布的话还需要在应用程序配置中加入spring的后缀映射。
然后我们就可以通过 http://../…/*.spring来访问我们的网站