MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥。今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个更加清晰的认识。
本文原创地址:http://www.cnblogs.com/landeanfen/p/6000978.html
MVC源码学习系列文章目录:
- MVC系列——MVC源码学习:打造自己的MVC框架(一)
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
- MVC系列——MVC源码学习:打造自己的MVC框架(四:自定义视图)
这篇博主打算从零开始一步一步来加上MVC里面用到的一些技术,整篇通过三个版本,逐步完善。
一、版本一:搭建环境,实现MVC请求
通过上篇的介绍,我们知道,MVC里面两个最核心的部件:MvcHandler和UrlRoutingModule。现在我们就来一步一步实现它们。为了更加真实,我们完全从零开始。
1、新建一个类库项目,我们暂且命名为Swift.MVC.
2、新建MvcHandler和UrlRoutingModule
我们新建两个文件,然后实现IHttpHandler和IHttpModule。我们知道这两个接口都在System.Web里面,首先我们在类库项目里面引用Syste.Web这个dll,然后来看具体的代码。
MvcHandler.cs代码:
namespace Swift.MVC
{
public class MvcHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
} public void ProcessRequest(HttpContext context)
{
context.Response.Write("当前页面地址:" + context.Request.Url.AbsoluteUri + " ");
context.Response.Write("Hello MVC");
}
}
}
UrlRoutingModule.cs代码:
namespace Swift.MVC
{
public class UrlRoutingModule : IHttpModule
{
public void Dispose()
{
//throw new NotImplementedException();
} public void Init(HttpApplication app)
{
app.PostResolveRequestCache += app_PostResolveRequestCache;
} void app_PostResolveRequestCache(object sender, EventArgs e)
{
var app = (HttpApplication)sender; app.Context.RemapHandler(new MvcHandler());
}
}
}
如果你看过博主的上篇,这个应该很好理解。UrlRoutingModule注册PostResolveRequestCache事件,通过这个事件拦截当前的请求,拦截到请求之后,再交由MvcHandler去处理当前的http请求。整个过程就是这么简单,我们最最基础的“框架”就搭好了。
3、新建一个空的Web项目测试Swift.MVC
第一步,新建一个空的Web项目,添加对Swift.MVC的引用,或者直接将Swift.MVC.dll拷贝到web项目的bin目录下面,两种方式都行,这里为了方便测试,我们直接添加解决方案中的项目引用。
第二步,配置Web项目的web.config文件。上篇我们就介绍过,HttpHandler和HttpModule的实现类要生效,就必须要在Web.config里面注册。注册之后整个Web.config的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web> <system.webServer>
<handlers>
<add name="swifthandler" verb="*" path="*" type="Swift.MVC.MvcHandler, Swift.MVC" preCondition="integratedMode" />
</handlers>
<modules>
<add name="swiftmodule" type="Swift.MVC.UrlRoutingModule, Swift.MVC" preCondition="integratedMode" />
</modules>
</system.webServer>
</configuration>
得到结果
这里博主想要说明两点:
- 如果你调试程序你会发现,app_PostResolveRequestCache()和ProcessRequest()都进了两遍,这是因为在web.config里面配置的是所有的请求都会被拦截,添加监视发现原来当我们访问http://localhost:16792/Home/Index地址的时候实际上是发了两次请求:
- 上篇我们就介绍过 app.Context.RemapHandler(new MvcHandler()); 这一句表示将当前请求交给Mvchandler这个去处理,既然是这里指定的MvcHandler,那我们在Web.config里面配置的 <handlers> 节点还有什么意义呢?也就是说,这里配置的即使是另外一个HttpHandler,那么最终程序还是会转给MvcHandler,是不是这样呢?既然我们感觉这里的handlers节点配置了也没用,那我们将handlers节点去掉再试试呢?结果是去掉handlers节点之后,仍然得到的是上面的结果。这一点说明app.Context.RemapHandler()这一句的优先级要比web.config里面的handlers节点的高。
这里通过以上实现和配置,我们的Swift.MVC已经具有处理http请求的能力,但还不能算一个完整意义上的框架,下面来继续完善。
二、版本二:完善MvcHandler和UrlRoutingModule
这个版本,UrlRoutingModule我们还是沿用的System.Web.Routing里面的机制,我们主要来看看MvcHandler这部分的实现。
1、UrlRoutingModule的完善
UrlRoutingModule.cs的完整代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Routing; namespace Swift.MVC
{
public class UrlRoutingModule : IHttpModule
{
#region Property
private RouteCollection _routeCollection; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
Justification = "This needs to be settable for unit tests.")]
public RouteCollection RouteCollection
{
get
{
if (_routeCollection == null)
{
_routeCollection = RouteTable.Routes;
}
return _routeCollection;
}
set
{
_routeCollection = value;
}
}
#endregion public void Dispose()
{
//throw new NotImplementedException();
} public void Init(HttpApplication app)
{
app.PostResolveRequestCache += app_PostResolveRequestCache;
} void app_PostResolveRequestCache(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
//0.将HttpContext转换为HttpContextWrapper对象(HttpContextWrapper继承HttpContextBase)
var contextbase = new HttpContextWrapper(app.Context);
PostResolveRequestCache(contextbase);
} public virtual void PostResolveRequestCache(HttpContextBase context)
{
//1.传入当前上下文对象,得到与当前请求匹配的RouteData对象
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData == null)
{
return;
}
//2.从RouteData对象里面得到当前的RouteHandler对象。
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
return;
} //3.根据HttpContext和RouteData得到RequestContext对象
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext; //4.根据RequestContext对象得到处理当前请求的HttpHandler(MvcHandler)。
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
return;
} //5.请求转到HttpHandler进行处理(进入到ProcessRequest方法)。这一步很重要,由这一步开始,请求才由UrlRoutingModule转到了MvcHandler里面
context.RemapHandler(httpHandler);
}
}
}
上述代码基本都是从Framework源码里面拷贝出来的,注释中的0、1、2、3、4、5分别对应着MVC路由过程中的各个步骤,详见上篇。
这里我们自定义了一个实现IRouteHandler的类型,用来返回处理请求的HttpHandler是哪个,比如这里我们定义的MvcRouteHandler返回的HttpHandler是MvcHandler。它的代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Routing; namespace Swift.MVC
{
public class MvcRouteHandler:IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MvcHandler();
}
}
}
2、MvcHandler部分的完善
首先还是抛出MvcHandler.cs的源码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web; namespace Swift.MVC
{
public class MvcHandler : IHttpHandler
{
public virtual bool IsReusable
{
get { return false; }
} public virtual void ProcessRequest(HttpContext context)
{
//写入MVC的版本到HttpHeader里面
//AddVersionHeader(httpContext);
//移除参数
//RemoveOptionalRoutingParameters(); //步骤1.从上下文的Request.RequestContext中取到RouteData对象。这里和UrlRoutingModule里面的context.Request.RequestContext = requestContext;对应。
var routeData = context.Request.RequestContext.RouteData; //步骤2.从当前的RouteData里面得到请求的控制器名称
string controllerName = routeData.GetRequiredString("controller"); //步骤3.得到控制器工厂
IControllerFactory factory = new SwiftControllerFactory(); //步骤4.通过默认控制器工厂得到当前请求的控制器对象
IController controller = factory.CreateController(context.Request.RequestContext, controllerName);
if (controller == null)
{
return;
} try
{
//步骤5.执行控制器的Action
controller.Execute(context.Request.RequestContext);
}
catch { }
finally
{
//步骤6.释放当前的控制器对象
factory.ReleaseController(controller);
} }
}
}
关于上述代码,我们说明以下几点。
2.1、关于控制器工厂
上述代码注释中的步骤1、2不难理解,就是从配置的路由规则中获取当前请求控制器的名称。要理解步骤3,需要先说一说MVC源码里面的控制器工厂。先来看看源码里面这段如何实现:
在源码里面的MvcHandler的ProcessRequest方法里面有这么一句: factory = ControllerBuilder.GetControllerFactory(); 。在MvcHandler里面ControllerBuilder这样定义:
internal ControllerBuilder ControllerBuilder
{
get
{
if (_controllerBuilder == null)
{
_controllerBuilder = ControllerBuilder.Current;
}
return _controllerBuilder;
}
set { _controllerBuilder = value; }
}
原来在MvcHandler中创建控制器工厂并不是直接使用IControllerFactroy的实现,而是使用了ControllerBuilder这个对象,这个对象采用了单例模式的实现;MvcHandler通过ControllerBuilder对象获取到一个实例,然后通过ControllerBuilder创建出IControllerFactory实现,ControllerBuilder管理着IControllerFactory的创建过程。
关于ControllerBuilder里面的GetControllerFactory()方法的实现,我们不必细究,但是我们需要知道的是在MVC里面有一个默认的控制器工厂的实现类DefaultControllerFactory。我们来看看
IControllerFactory接口的定义:
public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
void ReleaseController(IController controller);
}
DefaultControllerFactory的定义:
public class DefaultControllerFactory : IControllerFactory
{
public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
} if (String.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
} Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}
public virtual void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
} //...... }
上述的两个方法CreateController()和ReleaseController()通过名字都可以很好理解,分别对应着创建控制器和释放控制器。
了解了上述MVC里面控制器工厂的实现细节,我们自己也来建一个自己的控制器工厂,不过为了简化,我们这里直接去new了一个工厂的实现类。先来看看我们Swift.MVC的控制器工厂。
控制器工厂接口:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Routing; namespace Swift.MVC
{
//控制器创建工厂
public interface IControllerFactory
{
//创建控制器
IController CreateController(RequestContext requestContext, string controllerName); //释放控制器
void ReleaseController(IController controller);
}
}
控制器工厂实现类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; namespace Swift.MVC
{
public class SwiftControllerFactory:IControllerFactory
{
#region Public
//通过当前的请求上下文和控制器名称得到控制器的对象
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
} if (string.IsNullOrEmpty(controllerName))
{
throw new ArgumentException("controllerName");
}
//得到当前的控制类型
Type controllerType = GetControllerType(requestContext, controllerName);
if (controllerType == null)
{
return null;
}
//得到控制器对象
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
} //释放控制器对象
public void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion #region Privates
//得到当前请求的控制器实例
private IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
var oRes = Activator.CreateInstance(controllerType) as IController;
return oRes;
} //得到当前请求的控制器类型
private Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//从路由配置信息里面读取命名空间和程序集
object routeNamespaces;
object routeAssembly;
requestContext.RouteData.Values.TryGetValue("namespaces", out routeNamespaces);
requestContext.RouteData.Values.TryGetValue("assembly", out routeAssembly); //通过反射得到控制器的类型
var type = Assembly.Load(routeAssembly.ToString()).GetType(routeNamespaces.ToString() + "." + controllerName + "Controller"); return type;
}
#endregion
}
}
这里博主主要用到了反射去实例化控制器实例。
2.2、控制器的父类实现
上述介绍了控制器工厂的实现。除了控制器工厂,还有我们的控制器接口以及父类的相关实现。
控制器接口的定义:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Routing; namespace Swift.MVC
{
public interface IController
{
void Execute(RequestContext requestContext);
}
}
控制器抽象Base类的实现:(这个抽象类的作用更多在于定义一些约束、检查之类)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Swift.MVC
{
//这个类主要定义约束
public abstract class ControllerBase:IController
{ public abstract void Execute(System.Web.Routing.RequestContext requestContext);
}
}
控制器抽象子类的实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Swift.MVC
{
public abstract class Controller:ControllerBase,IDisposable
{
public override void Execute(System.Web.Routing.RequestContext requestContext)
{
//反射得到Action方法
Type type = this.GetType();
string actionName = requestContext.RouteData.GetRequiredString("action");
System.Reflection.MethodInfo mi = type.GetMethod(actionName); //执行该Action方法
mi.Invoke(this, new object[] { });//调用方法
} public void Dispose()
{
//throw new NotImplementedException();
}
}
}
这里让Controller类实现IDispose接口,照应了上文控制器工厂里面的ReleaseController()方法,主要起到释放资源的作用。
3、测试及代码释疑
由于上述代码用到了System.Web.Routing里面的组件,所以,需要在测试项目里面配置路由规则,这里需要注意一点,我们上面的MvcRouteHandler就是在这里注入进去的。在测试项目里面新建一个全局配置文件如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Web.Security;
using System.Web.SessionState; namespace MyTestMVC
{
public class Global : System.Web.HttpApplication
{ protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add("defaultRoute", new Route("{controller}/{action}/{id}", new RouteValueDictionary(new { controller = "Home", action = "Index", id = "", namespaces = "MyTestMVC.Controllers", assembly = "MyTestMVC" }), new Swift.MVC.MvcRouteHandler()));
} protected void Application_BeginRequest(object sender, EventArgs e)
{ }
}
}
然后在测试项目里面模拟MVC新建一个Controllers文件夹,里面新建一个测试的控制器HomeController:
using Swift.MVC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MyTestMVC.Controllers
{
public class HomeController : Controller
{
public void Index()
{
HttpContext.Current.Response.Write("Hello MVC");
}
}
}
然后启动项目,访问http://localhost:16792/Home/Index。运行过程以及代码释疑如下:
(1)来看看 RouteData routeData = this.RouteCollection.GetRouteData(context); 这一句
通过上图可知,this.RouteCollection里面保存的是上述全局配置文件里面添加进去的路由规则,然后调用GetRouteData(context)方法,传入当前请求的上下文,得到当前请求的RouteData对象,我们可以看到在这个RouteData对象里面,已经包含了当前请求的控制器和action的名称。
(2)监视 IRouteHandler routeHandler = routeData.RouteHandler;
通过上图可以看到在routeData里面我们的RouteHandler已经是MvcRouteHandler对象了,还记得我们在全局配置文件里面有这样一个配置:
RouteTable.Routes.Add("defaultRoute", new Route("{controller}/{action}/{id}", new RouteValueDictionary(new { controller = "Home", action = "Index", id = "", namespaces = "MyTestMVC.Controllers", assembly = "MyTestMVC" }), new Swift.MVC.MvcRouteHandler()));
在Add方法的最后需要传一个IRouteHandler的对象,我们上文定义过一个MvcRouteHandler去实现了IRouteHandler,这个MvcRouteHandler在这里就派上用场了,原来我们的RouteHandler是可以自定义的。就是因为这里配置过这个,所以在GetRouteData()方法里面,就将MvcRouteHandler对象给了routeData对象的RouteHandler属性,终于知道这里的MvcRouteHandler是如何过来的了。这里可配置IRouteHandler也说明了MVC原理的灵活性,我们可以自定义RouteHandler,然后再IRouteHandler接口的GetHttpHandler()方法里面自定义处理当前请求的HttpHandler。
(3) RequestContext requestContext = new RequestContext(context, routeData);context.Request.RequestContext = requestContext; 这两句看上去不起眼,就是封装了一个RequestContext对象,然后将它给到了当前上下文的Request.RequestContext。实际上,这里非常重要,因为这个requestContext对象包含了我们当前请求的路由信息,后面MvcHandler里面需要从这里取到当前请求的控制器和Action的名称,待会看了后面的代码,你会更加清晰。
(4)再来看 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 这一句
上文注释(2) 里面说了,routeHandler对象实际上是一个MvcRouteHandler对象,当它调用GetHttpHandler(),看下定义即可明白:
public class MvcRouteHandler:IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MvcHandler();
}
}
这里就是返回了一个MvcHandler,用来处理Http请求。
(5) context.RemapHandler(httpHandler); 这一句自然不必多说,请当前拦截到的请求交给MvcHandler的ProcessRequest方法处理,这一句执行完成之后,请求便转入到MvcHandler的ProcessRequest方法里面。纵观上述几个过程,可以说是一环扣一环,每一句都有它的意义所在,最后封装完成之后,真正处理请求还是在MvcHandler里面。接下来我们来看看ProcessRequest里面的代码。
(6)下面我们来看看MvcHandler类里面ProcessRequest方法这一句: var routeData = context.Request.RequestContext.RouteData; 。还记得上述注释3中封装的RequestContext对象吗,没错,这里就用到了这个对象,我们从这个对象里面取到当前请求的RouteData对象。
(7) string controllerName = routeData.GetRequiredString("controller"); 这一句不难理解:取到当前请求的的Controller名称。结果如下:
(8)得到控制器工厂这个没什么说的,为了简化,我们直接new了一个默认的控制器工厂。下面重点来看看 IController controller = factory.CreateController(context.Request.RequestContext, controllerName); 这一句。在控制器工厂的实现类里面实现了CreateController()这个方法。
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
} if (string.IsNullOrEmpty(controllerName))
{
throw new ArgumentException("controllerName");
}
//得到当前的控制类型
Type controllerType = GetControllerType(requestContext, controllerName);
if (controllerType == null)
{
return null;
}
//得到控制器对象
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
} //得到当前请求的控制器实例
private IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
var oRes = Activator.CreateInstance(controllerType) as IController;
return oRes;
} //得到当前请求的控制器类型
private Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//从路由配置信息里面读取命名空间和程序集
object routeNamespaces;
object routeAssembly;
requestContext.RouteData.Values.TryGetValue("namespaces", out routeNamespaces);
requestContext.RouteData.Values.TryGetValue("assembly", out routeAssembly); //通过反射得到控制器的类型
var type = Assembly.Load(routeAssembly.ToString()).GetType(routeNamespaces.ToString() + "." + controllerName + "Controller"); return type;
}
原理不难理解,主要还是反射,因为我们当前请求的控制器类在测试项目里面,所以反射的时候需要指定当前测试项目的程序集,通过这里的代码可以看出,在UrlRoutingModule里面封装的RequestContext对象实在是太重要了,因为各个地方都需要用到它。博主觉得这里还有待优化,等想到更好的办法再来逐步优化。此步得到结果:
(9)得到控制器对象之后,就是执行Action方法了: controller.Execute(context.Request.RequestContext); 。这里博主按照源码里面的构造封装了IController、ControllerBase、Controller三个级别的接口以及父类。Execute方法的实现在Controller里面:
public abstract class Controller:ControllerBase,IDisposable
{
public override void Execute(System.Web.Routing.RequestContext requestContext)
{
//反射得到Action方法
Type type = this.GetType();
string actionName = requestContext.RouteData.GetRequiredString("action");
System.Reflection.MethodInfo mi = type.GetMethod(actionName); //执行该Action方法
mi.Invoke(this, new object[] { });//调用方法
}
}
这里再次用到了RequestContext对象,由此可以看出,RequestContext对象几乎贯穿整个MvcHandler,再次应征了上述注释(3)中说的它的重要性。
上述代码就是通过反射Action方法,然后执行该方法,之后请求就会尽到我们HomeController的Index()方法里面。
(10)执行Index()方法
请求进入到Index()方法之后,然后就是从Model里面获取数据,再然后就是返回View,整个MVC的原理就是如此。当然博主这里的Swift.MVC还只是将请求转到了Index里面,剩余的Model可以自己写,但是View的部分还完全没有,待有时间完善。
(11)执行完Action之后,最后就是释放当前的Controller对象了。在finally里面有这么一句: factory.ReleaseController(controller); 。还是来看看ReleaseController方法的定义:
//释放控制器对象
public void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
至此,我们通过地址http://localhost:16792/Home/Index访问,整个请求的开始、执行、资源释放就基本结束。现在回过头来理解这个原理,你觉得还难吗~~
4、向Swift.MVC里面加入Model
如果你还嫌上述例子太简单,我们可以来个稍微复杂点的例子。我们向测试项目里面引入jquery、bootstrap等组件,添加Models文件夹,向下面加入User.cs
namespace MyTestMVC.Models
{
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Remark { get; set; }
}
}
然后我们向HomeController里面另一个Action定义如下:
public void BootstrapTest()
{
var lstUser = new List<User>();
lstUser.Add(new User() { Id = , UserName = "Admin", Age = , Address = "北京", Remark = "超级管理员" });
lstUser.Add(new User() { Id = , UserName = "张三", Age = , Address = "湖南", Remark = "呵呵" });
lstUser.Add(new User() { Id = , UserName = "王五", Age = , Address = "广西", Remark = "呵呵" });
lstUser.Add(new User() { Id = , UserName = "韩梅梅", Age = , Address = "上海", Remark = "呵呵" });
lstUser.Add(new User() { Id = , UserName = "呵呵", Age = , Address = "广东", Remark = "呵呵" }); string strUser = string.Empty;
foreach (var oUser in lstUser)
{
strUser += "<tr><td>" + oUser.Id + "</td><td>" + oUser.UserName + "</td><td>" + oUser.Age + "</td><td>" + oUser.Address + "</td><td>" + oUser.Remark + "</td></tr>";
} HttpContext.Current.Response.Write(@"
<html>
<head>
<link href='/Content/bootstrap/css/bootstrap.min.css' rel='stylesheet' />
<script src='/Content/jquery-1.9.1.min.js'></script>
<script src='/Content/bootstrap/js/bootstrap.min.js'></script>
</head>
<body>
<div class='panel-body' style='padding-bottom:0px;'>
<div class='panel panel-primary'>
<div class='panel-heading'>bootstrap表格</div>
<div class='panel-body'>
<table id='tbarrivequeue' class='table table-bordered table-striped'>
<thead>
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>年龄</th>
<th>地址</th>
<th>备注</th>
</tr>
</thead>
<tbody>
" + strUser + @"
</tbody>
</table>
</div>
</div>
</div> </div>
</body>
</html>");
得到结果:
按照MVC的机制,我们的Swift.MVC里面“M”和“C”都有了,就差“V”了,等有时间我们也来封装一个自己的“V”。
三、版本三:加入自己的路由模块
有了第二个版本的支持,博主已经对MVC的原理有了一个清晰的认识。在第三个版本里面,博主打算不用System.Web.Routing里面的属性和方法,完全自己去解析请求,封装上下文,执行MVC,这样才算是真正意义上自己的MVC。考虑到篇幅的问题,也给博主一些准备时间,第三个版本留在下篇发,有兴趣的园友可以先看看版本二,如果你能透彻理解版本二里面的原理,相信你对MVC 已经有一个清晰的认识了。
四、总结
至此,此篇到此结束,版本二里面我们MvcHandler基本上是完全重写的,虽然很多原理是参考MVC源码里面来进行的,但是它却和源码已经没有任何联系。而UrlRoutingModule部分仍然使用的System.Web.Routing里面的组件去解析路由的,这个将在第三个版本里面完善。源码已经在github上面开源,有兴趣可以看看:源码下载
如果你觉得本文能够帮助你,可以右边随意 打赏 博主,也可以 推荐 进行精神鼓励。你的支持是博主继续坚持的不懈动力。
本文原创出处:http://www.cnblogs.com/landeanfen/
欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利
MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)的更多相关文章
- 源码学习之ASP.NET MVC Application Using Entity Framework
源码学习的重要性,再一次让人信服. ASP.NET MVC Application Using Entity Framework Code First 做MVC已经有段时间了,但看了一些CodePle ...
- 【ASP.NET MVC系列】浅谈jqGrid 在ASP.NET MVC中增删改查
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
- Spring源码学习之IOC实现原理(二)-ApplicationContext
一.Spring核心组件结构 总的来说Spring共有三个核心组件,分别为Core,Context,Bean.三大核心组件的协同工作主要表现在 :Bean是包装我们应用程序自定义对象Object的,O ...
- 教你搭建SpringMVC框架( 附源码)
一.项目目录结构 二.SpringMVC需要使用的jar包 commons-logging-1.2.jar junit-4.10.jar log4j-api-2.0.2.jar log4j-core- ...
- SpringMVC源码学习:容器初始化+MVC初始化+请求分发处理+参数解析+返回值解析+视图解析
目录 一.前言 二.初始化 1. 容器初始化 根容器查找的方法 容器创建的方法 加载配置文件信息 2. MVC的初始化 文件上传解析器 区域信息解析器 handler映射信息解析 3. Handler ...
- 【网站国际化必备】Asp.Net MVC 集成Paypal(贝宝)快速结账 支付接口 ,附源码demo
开篇先给大家讲段历史故事,博主是湖北襄阳人.襄阳物华天宝,人杰地灵,曾用名襄樊.在2800多年的历史文化中出现了一代名相诸葛亮(卧龙),三国名士庞统(凤雏),魏晋隐士司马徽(水镜先生),唐代大诗人孟浩 ...
- Java集合源码学习(一)集合框架概览
>>集合框架 Java集合框架包含了大部分Java开发中用到的数据结构,主要包括List列表.Set集合.Map映射.迭代器(Iterator.Enumeration).工具类(Array ...
- 我们一起学习WCF 第一篇初识WCF(附源码供对照学习)
前言:去年由于工作需要我学习了wcf的相关知识,初期对wcf的作用以及为何用怎么样都是一知半解,也许现在也不是非常的清晰.但是通过项目对wcf的运用在脑海里面也算有了初步的模型.今天我就把我从开始wc ...
- Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析
Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThread ...
随机推荐
- [JSP]JSP 简介
1.1 概述 1.1.1 什么是 JSP? JSP 全称是 Java Server Pages,是一种动态网页开发技术. 它与 PHP.ASP.ASP.NET 等语言类似,运行在服务端的语言. JSP ...
- php内核分析(七)-扩展
这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux. 我们研究下反射这个扩展. 反射这个扩展目录是存在在:ext/reflection.其实里面的代码很简单.一个.h文件,一 ...
- ListView初探
一.ListView介绍 在Android开发中ListView是比较常用的控件,常用于以列表的形式显示数据集及根据数据的长度自适应显示. ListView通常有两个主要功能点: (1)将数据集填充到 ...
- HTML5 数据集属性dataset
有时候在HTML元素上绑定一些额外信息,特别是JS选取操作这些元素时特别有帮助.通常我们会使用getAttribute()和setAttribute()来读和写非标题属性的值.但为此付出的代价是文档将 ...
- 【CLR via C#】CSC将源代码编译成托管模块
下图展示了编译源代码文件的过程.如图所示,可用支持 CLR 的任何一种语言创建源代码文件.然后,用一个对应的编译器检查语法和分析源代码.无论选用哪一个编译器,结果都是一个托管模块(managedmod ...
- CString转换为LPSTR和LPSTR转化为CString
一.CString转换为LPSTR 方法一: CString strFileName LPSTR lpstr - strFileName.GetBuffer(); strFileName.Releas ...
- JHipster框架的简要搭建与说明
JHipster的亮点 风头超劲,席卷欧美,最新全能Java Web开发程式产生器 (java web generator). 由Java专家累积的开发经验,配上各类实用的框架技术,去繁取精的运用,全 ...
- Struts2入门(七)——Struts2的文件上传和下载
一.前言 在之前的随笔之中,我们已经了解Java通过上传组件来实现上传和下载,这次我们来了解Struts2的上传和下载. 注意:文件上传时,我们需要将表单提交方式设置为"POST" ...
- 苹果 OS X 系统U盘重装-抹盘重装、系统盘制作
鉴于前段时间系统出了点问题,然后直接将盘抹了,来个彻底干净的系统重装.这里敲下过程.(网络恢复太慢了,我整整一个晚上竟然没down下来,恼怒了,直接U盘装) First,系统盘制作: 1.首先需要有: ...
- java代码走查审查规范
分类 重要性 检查项 备注 命名 重要 命名规则是否与所采用的规范保持一致? 成员变量,方法参数等需要使用首字母小写,其余单词首字母大写的命名方式,禁止使用下划线(_)数字等方式命名不 ...