借助Web模拟程序 了解ASP.NET MVC运行

简介:

ASP.NET MVC的路由系统通过对HTTP请求的解析得到表示Controller、Action和其他相关的数据,并以此为依据激活Controller对象,调用相应的Action方法,并将方法返回的ActionResult写入HTTP回复中。为了更好的演示其实现原理,我创建一个简单的ASP.NET Web应用来模拟ASP.NET MVC的路由机制。这个例子中的相关组件基本上就是根据ASP.NET MVC的同名组件设计的,只是我将它们进行了最大限度的简化,因为我们只需要用它来演示大致的实现原理而已(源代码从这里下载)。

        一个通过查询字符串表示Controller和Action的“MVC”程序

  如下图所示,我们的Web应用非常简单。HomeController.cs为定义Controller类型的文件,而Index.html表示HomeController中名称为Index的Action对应的View。我们按照ASP.NET MVC的原理,通过解析请求URL得到Controller和Action的名称。如果Controller为Home,则激活HomeController,如果当前的Action为Index,则将Index.html这个静态文件的内容作为HTTP回复返回。

  借助模拟程序  了解ASP.NET MVC运行

  我不想定义复杂的解析Controller和Action的逻辑,再这里我直接通过请求URL相应的查询字符串controler和action表示Controller和Action的名称。也就是说如果通过浏览器访问地址http://localhost/mvcapp/?controller=Home&action=Index 可以访问到Index.html中的内容(注:我们并没有将Index.html作为站点的默认页面)。

  借助模拟程序  了解ASP.NET MVC运行

  接下来我简单的介绍一下是哪些组建促使这个简单的ASP.NET Web应用能够按照MVC的模式来执行。为了使你能够在真正的ASP.NET MVC找到匹配的组件,我们采用了相同的接口和类型名称。

  通过Route解析HTTP请求获得路由信息

  我定义了如下一个RouteData类型表示解析HTTP请求得到的Controller和Action等信息。Assemblies和Namespaces表示需要引入的命名空间和程序集,这是因为URL中只能解析出Controller的类型名称,需要相应的命名空间采用得到它的类型全名。如果对应的程序集不曾加载,还需要加载相应的程序集。

1 public   class  RouteData   
2 : {   
3 :      public   string  Controller {  get set ; }   
4 :      public   string  Action {  get set ; }   
5 :      public  IList < string >  Assemblies {  get private   set ; }   
6 :      public  IList < string >  Namespaces {  get private   set ; }   
7 :      public  IRouteHandler RouteHandler {  get set ; }   
8 :     
9 :      public  RouteData( string  controller,  string  action, IRouteHandler routeHandler)  
10 :     {  
11 :         this.Controller  =  controller;  
12 :         this.Action  =  action;  
13 :         this.RouteHandler  =  routeHandler;  
14 :         this.Namespaces  =  RouteTable.Namespaces;  
15 :         this.Assemblies  =  RouteTable.Assemblies;  
16 :     }  
17 : }

  真正实现对HTTP请求进行解析并得到RouteData的Route继承自基类RouteBase。我们还定义个了一个表示Route集合的RouteCollection类型,它的GetRouteData方法对集合的所有Route对象进行遍历,并调用其GetRouteData方法。如果得到的RouteData不为空,则返回之。

1 public  abstract  class  RouteBase   
2 : {   
3 :      public  abstract RouteData GetRouteData(HttpContextBase httpContext);   
4 : }   
5 :     
6 public   class  RouteCollection: Collection < RouteBase >    
7 : {   
8 :      public  RouteData GetRouteData(HttpContextBase httpContext)   
9 :     {  
10 :         foreach (RouteBase route  in  this)  
11 :         {  
12 :             var routeData  =  route.GetRouteData(httpContext);  
13 :              if  (null ! =  routeData)  
14 :             {  
15 :                  return  routeData;  
16 :             }  
17 :         }  
18 :          return  null;  
19 :     }  
20 : }

  和ASP.NET MVC一样,我们定义了如下一个RouteTable对象,其静态属性正是一个RouteCollection对象。两个静态属性Namespaces和Assemblies为命名空间和程序集名称的全局维护。

1 public   class  RouteTable   
2 : {   
3 :      static  RouteTable()  
4 :     {   
5 :         Routes  =   new  RouteCollection();   
6 :         Namespaces  =   new  List < string > ();   
7 :         Assemblies  =   new  List < string > ();   
8 :     }   
9 :      public   static  RouteCollection Routes {  get private   set ; }  
10 :      public   static  IList < string >  Namespaces {  get private   set ; }  
11 :      public   static  IList < string >  Assemblies {  get private   set ; }  
12 : }

  而我们实例中完成基于查询字符串的Controller和Action解析的QueryStringRoute对应如下。在GetRouteData方法中,除了根据查询字符解析并初始化Controller和Action名称之外,还将RouteHandler指定为MvcRouteHandler。而MvcRouteHandler得GetHttpHandler方法直接返回的是根据RequestContext创建的MvcHandler对象。

1 public   class  QueryStringRoute : RouteBase   
2 : {   
3 :      public  override RouteData GetRouteData(HttpContextBase httpContext)   
4 :     {   
5 :          if  (httpContext.Request.QueryString.AllKeys.Contains( " controller " &&    
6 :             httpContext.Request.QueryString.AllKeys.Contains( " controller " ) )   
7 :         {   
8 :              string  controller  =  httpContext.Request.QueryString[ " controller " ];   
9 :              string  action  =  httpContext.Request.QueryString[ " action " ];  
10 :             IRouteHandler routeHandler  =   new  MvcRouteHandler();  
11 :              return   new  RouteData(controller, action, routeHandler);                 
12 :         }  
13 :          return  null;  
14 :     }  
15 : }  
16 :    
17 public   class  MvcRouteHandler: IRouteHandler  
18 : {  
19 :      public  IHttpHandler GetHttpHandler(RequestContext requestContext)  
20 :     {  
21 :          return   new  MvcHandler(requestContext);  
22 :     } 
  23 : }

  在Global.asax中注册Route

  通过上面定义的RouteTable类型,我们在Global.asax中按照如下的方式在应用启动的时候QueryStringRoute对象添加到RouteTable的静态属性Routes表示的Route列表中。同时为需要的命名空间和程序集名称进行初始化,以辅助后续步骤中对Controller的创建。

1 public   class  Global : System.Web.HttpApplication   
2 : {   
3 :      protected  void Application_Start( object  sender, EventArgs e)   
4 :     {   
5 :         RouteTable.Routes.Add( new  QueryStringRoute());   
6 :         RouteTable.Assemblies.Add( " MvcApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null " );   
7 :         RouteTable.Namespaces.Add( " Artech.MvcApp " );   
8 :     }   
9 : }

  Route的执行

  通过RouteTable的Routes属性表示的Route列表对请求的解析和路由信息的获取是通过自定义的HttpModule来实现的,它的类型为UrlRoutingModule。如下面的代码片断所示,UrlRoutingModule注册了HttpApplication的PostResolveRequestCache事件,并在该事件触发的时候调用Route列表的GetRouteData方法,并根据得到RouteData创建RequestContext。最后通过RouteData的RouteHandler得到真正用于处理该请求的HttpHandler对象,并对其进行映射。这意味着后续将会采用这个映射的HttpHandler进行请求的处理。

1 public   class  UrlRoutingModule: IHttpModule  
2 : {   
3 :      public  void Dispose() { }   
4 :      public  void Init(HttpApplication context)   
5 :     {   
6 :         context.PostResolveRequestCache  +=  (sender, args)  =>    
7 :             {   
8 :                 HttpContextWrapper contextWrapper  =   new  HttpContextWrapper(context.Context);   
9 :                 HttpContextBase httpContext  =  (HttpContextBase)contextWrapper;  
10 :                 RouteData routeData  =  RouteTable.Routes.GetRouteData(httpContext);  
11 :                  if  (null  ==  routeData)  
12 :                 {  
13 :                      return ;  
14 :                 } 
15 :                 RequestContext requestContext  =   new  RequestContext { HttpContext  =  httpContext, RouteData  =  routeData };                       16 :                 httpContext.RemapHandler(routeData.RouteHandler.GetHttpHandler(requestContext));   17 :             };   18 :     }   19 : }

 

通过MvcHandler处理请求

  在UrlRoutingModule映射的实际上是具有如下定义的MvcHandler,它具有一个RequestContext属性通过构造函数进行初始化。在ASP.NET MVC中,真正的请求处理体现在根据路由信息创建Controller,并执行相应的Action方法。这两个步骤体现的ProcessRequest方法中。

1 public   class  MvcHandler: IHttpHandler   
2 : {   
3 :      public  RequestContext RequestContext{ get private   set ;}   
4 :      public  IControllerFactory ControllerFactory   
5 :     {   
6 :          get  {  return  ControllerBuilder.Current.GetControllerFactory(); }   
7 :     }   
8 :      public  MvcHandler(RequestContext requestContext)   
9 :     {  
10 :         this.RequestContext  =  requestContext;  
11 :     }  
12 :      public  bool IsReusable  
13 :     {  
14 :          get  {  return   false ; }  
15 :     }  
16 :      public  void ProcessRequest(HttpContext context)  
17 :     {  
18 :         RouteData routeData  =  this.RequestContext.RouteData;  
19 :         var controller  =   this.ControllerFactory.CreateController(this.RequestContext, routeData.Controller);  
20 :         controller.Execute(this.RequestContext);  
21 :     }  
22 : }

        Controller实现了具有如下定义的接口IController,所有Action方法都通过Execute方法执行,该方法的参数的表示当前请求上下文的RequestContext对象。IController通过相应的Controller工厂创建,下面的代码同时也定义了Controller工厂接口的定义。

1 public   interface  IController   
2 : {   
3 :     void Execute(RequestContext requestContext);   
4 : }   
5 public   interface  IControllerFactory   
6 : {   
7 :     IController CreateController(RequestContext requestContext,  string  controllerName);   
8 : }

  我们定义了如下一个简单名称为DefaultController,它的Execute方法定义很简单:通过包含在RequestContext的RouteData得到当前的Action,并将它作为方法名得到相应的MethodInfo对象,滨个通过反射调用它得到一个ActionResult对象,最后执行ActionResult的ExecuteResult方法。该方法的参数是基于RequestContext创建的另一个上下文ControllerContext。

1 public   class  DefaultController : IController   
2 : {   
3 :      public  void Execute(RequestContext requestContext)   
4 :     {  
5 :          string  action  =  requestContext.RouteData.Action;   
6 :         MethodInfo method  =  this.GetType().GetMethod(action);  
7 :         ActionResult result  =  (ActionResult)method.Invoke(this, null);   
8 :         ControllerContext controllerContext  =   new  ControllerContext   
9 :         {  
10 :             RequestContext  =  requestContext  
11 :         };  
12 :         result.ExecuteResult(controllerContext);  
13 :     }  
14 : }

  我们定义了具有如下定义的Controller工厂类DefaultControllerFactory。创建Controller的逻辑也不复杂:通过RouteData表示的Controller名称得到相应的Controller类型,通过反射创建Controller对象。由于RouteData中只包含Controller的名称,所以需要通过命名空间和程序集的辅助才能解析出真正的类型。

1 class  DefaultControllerFactory : IControllerFactory   
2 : {   
3 :      public  IController CreateController(RequestContext requestContext,  string  controllerName)   
4 :     {   
5 :         RouteData routeData  =  requestContext.RouteData;   
6 :          string  controllerType  =   string .Format( " {0}Controller " , controllerName);   
7 :         IController controller;   
8 :         controller  =  this.CreateControler(controllerType);   
9 :          if  (null ! =  controller)  
10 :         {  
11 :              return  controller;  
12 :         }  
13 :         foreach ( string   assembly   in  routeData.Assemblies)  
14 :         {  
15 :             controller  =  this.CreateControler(controllerType,  assembly );  
16 :              if  (null ! =  controller)  
17 :             {   18 :                  return  controller;  
19 :             }  
20 :    
21 :             foreach ( string  ns  in  routeData.Namespaces)  
22 :             {  
23 :                 controllerType  =   string .Format( " {0}.{1}Controller " , ns, controllerName);  
24 :                 controller  =  this.CreateControler(controllerType,  assembly );  
25 :                  if  (null ! =  controller)  
26 :                 {  
27 :                      return  controller;  
28 :                 }  
29 :             }  
30 :         }  
31 :    
32 :          throw   new  InvalidOperationException( " Cannot locate the controller " );  
33 :     }  
34 :      private  IController CreateControler( string  controllerType,  string   assembly   =  null)  
35 :     {  
36 :         Type type  =  null;  
37 :          if  (null  ==   assembly )  
38 :         {  
39 :             type  =  Type.GetType(controllerType);  
40 :         }  
41 :          else   
42 :         {  
43 :             type  =   Assembly .Load( assembly ).GetType(controllerType);  
44 :         }  
45 :          if  (null  ==  type)  
46 :         {  
47 :              return  null;  
48 :         }  
49 :          return  Activator.CreateInstance(type)  as  IController;  
50 :     }  
51 : }

  将ActionResult写入Http回复

  Controller的Action方法的返回值为具有如下定义的ActionResult类型,通过ExecuteResult方法将相应的执行结果写入HTTP回复中。我定义了如下一个StaticViewResult,它根据RouteData中的Action信息找到匹配的.html静态文件,并将文件的内容写入HttpResponse。

1 public  abstract  class  ActionResult   
2 : {   
3 :      public  abstract void ExecuteResult(ControllerContext context);   
4 : }   
5 :     
6 public   class  StaticViewResult: ActionResult   
7 : {   
8 :      public  override void ExecuteResult(ControllerContext context)   
9 :     {  
10 :         context.RequestContext.HttpContext.Response.WriteFile(context.RequestContext.RouteData.Action  +   " .html " );  
11 :     }  
12 : }

  实例的配置和定义

  在我们的实例中定义的HomeController定义如下,在表示Action的Index方法中,直接返回一个StaticViewResult对象。

1 public   class  HomeController : DefaultController   
2 : {   
3 :      public  ActionResult Index()   
4 :     {   
5 :          return   new  StaticViewResult();   
6 :     }   
7 : }

        然后在配置中进行了针对UrlRoutingModule的注册,仅此而已。

1 < configuration >    
2 :    < system.webServer >    
3 :      < modules >    
4 :        < add name = " UrlRoutingModule "  type = " Artech.MvcRouting.UrlRoutingModule, Artech.MvcRouting " />    
5 :      </ modules >    
6 :    </ system.webServer >    
7 </ configuration >









本文转自 wws5201985 51CTO博客,原文链接:http://blog.51cto.com/wws5201985/735608,如需转载请自行联系原作者
目录
相关文章
|
20天前
|
前端开发 安全 Java
使用Java Web框架:Spring MVC的全面指南
【4月更文挑战第3天】Spring MVC是Spring框架的一部分,用于构建高效、模块化的Web应用。它基于MVC模式,支持多种视图技术。核心概念包括DispatcherServlet(前端控制器)、HandlerMapping(请求映射)、Controller(处理请求)、ViewResolver(视图解析)和ModelAndView(模型和视图容器)。开发流程涉及配置DispatcherServlet、定义Controller、创建View、处理数据、绑定模型和异常处理。
使用Java Web框架:Spring MVC的全面指南
|
1月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
29 0
|
1月前
mvc.net分页查询案例——mvc-paper.css
mvc.net分页查询案例——mvc-paper.css
5 0
|
1月前
|
存储 设计模式 前端开发
请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
【2月更文挑战第26天】【2月更文挑战第89篇】请解释 Web 应用程序的 MVC(模型-视图-控制器)架构。
|
1月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
95 5
|
2月前
|
设计模式 存储 前端开发
Java Web开发中MVC设计模式的实现与解析
Java Web开发中MVC设计模式的实现与解析
|
2月前
|
前端开发 Java 应用服务中间件
【JavaEE进阶】 初识Spring Web MVC
【JavaEE进阶】 初识Spring Web MVC
|
2月前
|
JavaScript 前端开发 Java
MooTools、Backbone、Sammy、Cappuccino、Knockout、JavaScript MVC、Google Web Toolkit、Google Closure、Ember、Batman 以及 Ext JS。
MooTools、Backbone、Sammy、Cappuccino、Knockout、JavaScript MVC、Google Web Toolkit、Google Closure、Ember、Batman 和 Ext JS 都是 JavaScript 框架,用于开发 Web 应用程序。它们分别提供了不同的功能和特性,以帮助开发者更高效地构建和维护 Web 应用程序。
17 2
|
2月前
|
SQL 开发框架 .NET
ASP.NET WEB+EntityFramework数据持久化——考核练习库——1、用户管理系统(考点:查询列表、增加、删除)
ASP.NET WEB+EntityFramework数据持久化——考核练习库——1、用户管理系统(考点:查询列表、增加、删除)
67 0
|
2月前
|
SQL 开发框架 前端开发
ASP.NET WEB项目中GridView与Repeater数据绑定控件的用法
ASP.NET WEB项目中GridView与Repeater数据绑定控件的用法
34 0