ASP.NET MVC的流程讲解
开始想这个标题的时候,很是忧郁到底叫什么标题会比较好哪,最后还是确定为讲解,虽然略显初级,但是很多概念,想通了还是比较有价值的。废话略过,开始!
1、MVC的基本开发流程
2、webform和MVC的选择
3、MVC的内部过程
1、MVC的开发流程
MVC的出现时微软在2009年左右开始提出的网站开发的新的发展方向,这个方面的开发官方解释是可以比较好的实现三层分离,而且分离之后,可以实现复用等相关好处,通常人们列举的例子是ui方面可以同时支持HTML网络或者WAP网络。但是其实个人的理解是,动态网站的开发经过不断地证实和发展,java的struts模型,可以提高开发速度,也可以降低差多,是比较好的框架,而微软也需要提供自己的开发框架。不能够只是一个界面一个界面的设计方式,设计模式逐步进入到了web开发的领域。
MVC使用vs2010进行开发时(这里介绍的是MVC2),首先需要选在一个模板,然后vs2010会帮忙创建好对应的目录结构。
喎? f/ware/vc/"="" target="_blank" class="keylink">vcD4KPHA+w7+49sS/wry1xLv5sb65psTco7o8L3A+CjxwPkNvbnRlbnSjutb30qrTw9Paw+jK9mNzc9auwOC1xNfK1LQ8L3A+CjxwPkNvbnRyb2xsZXJzo7rW99Kqvs3Kx2NvbnRyb2xsZXK1xLTmt8XOu9bDo6y0tL2oY29udHJvbGxlcsqxo6y2vMrH0OjSqtTauMPEv8K8tLS9qLXEoaM8L3A+CjxwPk1vZGVsc6O61vfSqr7NysdlbnRpdHm1xL7fzOXOu9bDo6zS1LywuPplbnRpdHnP4LnYtcSy2df3wOA8L3A+CjxwPlNjcmlwdHOjumphdmFzY3JpcHS9xbG+tOa3xbXEzrvWwzwvcD4KPHA+Vmlld3OjurjDsr+31tb30qrKx7fF1sN2aWV3z9TKvrK/t9a1xDwvcD4KPHA+R2xvYmFsLmFzYXijusS/x7DAtL+0o6y4w7K/t9bW99Kqvs3Kx8K308nJ6NbDPC9wPgo8cD5XZWIuY29uZmlno7q4w8Xk1sPOxLz+tvjS0TwvcD4KPHA+PGJyPgo8L3A+CjxwPiAgICAgICAgILTTv6q3orXEwfezzLe9w+bAtL+0o6xNVkO1xL+qt6K3vcq9o6y78tXfy7XLvL+8tcS3vcq9s/bP1sHLseS7r6Os1NpNVkO1sdbQo6zQ6NKqwO294tK7uPbW2NKqtcS148rHo7o8L3A+CjxwPkNvbnRyb2xsZXKyxcrHz7XNs7XE1tDQxKOs0rvH0M6nyMZDb250cm9sbGVy1bm/qqGjPC9wPgo8cD4gPC9wPgo8cD5Nb2RlbKO6y/nOvcSj0M2jrL/J0tTA7b3izqrK/b7do6zV4rj2yv2+3b/J0tTKx8r9vt2/4tbQttTTprXEse3W0LXEyv2+3aOs1eLW1sr9vt3Kx9a709DK9NDUo6y2+MO709C2r9f3tcSjrNXi1tbK/b7dzaizo9KysbuzxtauzqpFbnRpdHmjrLy0yrXM5aOss/3By9Xi1tbK/b7d1q7N4qOsTU9ETEXG8Mq8u7nSqrD8wKhJbnRlcmZhY2WjrNXi0Km907/atcTEv7HqysfM4bmpv8nS1L/Y1sZFbnRpdHm1xL3Tv9qx6te8o6zIu7rz1NrM4bmpyrXP1rXE1NjM5aOszaizo87Sw8ezxtauzqpNb2NrwOCjrM6qwcu3vbHjo6y/ycTcztLDx7u5u+HU2k1vZGVstbHW0LS0vai499bWZmFjdG9yeaOstNO2+Lzyu6+21M/ztcS0tL2ooaM8L3A+CjxwPiA8L3A+CjxwPkNvbnRyb2xsZXKjutXiuPayv7fWvs3Kx7rL0MTBy6OsxuTKtcv5zr26y9DEo6zKx8u1y/nT0LXEtKbA7aOsyKuyv86nyMbXxUNvbnRyb2xsZXLVub+qo6zL/LXE1vfSqrmk1/fKx7fDzsptb2RlbKOsu/HIocr9vt2686Osvauyzsr916q3orj4dmlld6OsyLu688jDdmlld7Htz9az9sC0oaPU2tXiwO/W99KqzeqzybXEuaTX99PQwb2146O6PC9wPgo8cD4xoaLU2r/Nu6e2y7fDzsrSu7j20rPD5rrzo6zQ6NKqzPjXqrW9Q29udHJvbGxlcrbU06a1xGFjdGlvbtbQyKWjrMi7uvPU2mFjdGlvbtbQtKbA7bbU06a1xHZpZXfP1Mq+s/bAtKGjPC9wPgo8cD4yoaIgzeqzyb/Nu6e1xLHttaXM4b27z+DTprSmwO2jrNKyvs3Kx0Zvcm2x7bWltKbA7aGjKLu5vMe1w9aux7C9srn9o6y21NPaSFRNTLb40dSjrNa709BGb3Jtse21pcq1z9bBy7/Nu6e2y7XE0MXPoreiy824+Lf+zvG2y6OsyLu689PJt/7O8bbLtKbA7c/gudi1xM/g06ajrNLyzqpNVkO1xMnovMbEv7Hqvs3Kx7fFxvrBy86iyO3UrdPQtcS3/s7xxve/2Lz+o6zS8rTL0rvH0LvYuenUrcq8o6yyydPDSFRNTLXEZm9ybbHttaW3vcq9yrXP1szhvbu6zc/gudi1xL/Y1sa0psDtoaMpPC9wPgo8cD4gPC9wPgo8cD5WaWV3o7q5y8P7y7zS5aOsuMOyv7fWvs3Kx8/WyrW1xLK/t9ajrNXiuPayv7fW0OjSqsqxv8y8x9ehtcTKx6Os1eK49nZpZXfL5Mi70rLKx2FzcHi1xNKzw+ajrLWrysfS0b6tt6LJ+sHLuPmxvtDUtcSx5Luvo6yyu9TZ09DL+c69tcRjb2RlYmVpbmS0+sLrwcujrNXiuPZ2aWV3tcTL+dPQseSzyb2rssnTw7vsus/KvbXEseSzyaOsxOO74dei0uK1vdXiuPayv7fWtcSx5LPJseTOqkhUTUzT60MjtcS77LrPo6y74bP2z9a63LbgtcQ8JSU+PCU9JT7A4CYjMjAyODQ7tcS0+sLroaO63LbgyMvG8Mq8ttTV4rj2sr+31tPQsrvNrLXEv6q3oqOsu+y6z7T6wuu21NPat9ay47K7wPujrLWrysfU2k1WQ9bQo6zS8s6qsrvJ5rywwt+8raOsy/nS1HZpZXe1xLHtz9ax5LXDvPK1paOsu+y6z7Hgs8y74bHkzqq/ydLUvdPK3LXEtKbA7be9yr2ho8HtzeKjrNXi1ta3vcq9tPjAtLXEusO0psrHo6zDwLmkv8nS1L3pyOvBy6Osy/vDx7XE0N64xLbU09qzzNDy1LHAtMu1o6zDu9PQyrLDtMzYsfCjrNKyyse3x7OjyN3S19axvdPS/cjrtcSho7T4wLS1xLu1tKbKx0dyaWR2aWV31eLW1se/tPO1xLf+zvHG97/YvP6xu7aqxvrBy6Gjy+TIu8rH1eLR+aOstavKx87SuPbIy771tcOjrNXiyse72LW9wct3ZWK/qreitcSxvtbKoaPL+7XEy7zP66Os0+tKU1AsUEhQtci1yLHkzqrSu9bCoaM8L3A+CjxwPiA8L3A+CjxwPkdvbGFiYWwuYXNheKO6wrfTybHto6zV4rj2sr+31r7NysfL+c69tcTIq77WwrfTybHtoaPU2k1WQ7/yvNzW0KOs1q7L+dLUyrXP1sHLTVZDuabE3KOs0ru49tbY0qq1xLjFxO7Kx8K308mx7aOsuMOyv7fWyrXP1sHLtdjWt7fDzsq1xLavzKyjrLK71NnM4bmpvt/M5dKzw+a1xLfDzsrEo8q9oaM8L3A+CjxwPiA8L3A+CjxwPtei0uKjurj4ztK1xLjQvvXKx6OsvMfXodTadmlld8S/wry6zW1vZGVsxL/CvNbQo6zM7bzT19PEv8K8o6zDv7j2Y29udHJvbGxlcrbU06a1xHZpZXejrLa8ysfSu7j2xL/CvM/CtcR2aWV3oaPV4rj2ysdNVkO/8rzcsunV0sqx19S2r8vRy/e1xKGjPC9wPgo8cD4gPC9wPgo8aDE+MqGid2ViZm9ybbrNTVZDtcTRodTxPC9oMT4KPHA+IDwvcD4KPHA+ICAgICAgICAg1eK49rK/t9a1xNX5wtujrM7Sz+u0086iyO2/qsq8zcaz9k1WQ7/yvNy686OstPO80r7N1Nqyu7zkts+1xMzWwtvXxaOssrvNrLXEyMujrLj4s/a1xL+0t6jSssrHsrvNrKOsvs3O0rj2yMu2+NHUo6zO0r71tcNNVkOyxcrHzrTAtMf3ysajrMrHysC959fuuvO0882stcS4+bG+oaO+obncd2ViIGZvcm21xMSjyr2jrMrHzqLI7b+qtLTQ1LXEtLTU7KOstavKx7HPvrl3ZWK/qreisrvKx86iyO3K17S0o6y63LbgyrG68qOstPPKxsv5x/e2+NLRoaPO0tXiwO/Wu8rHz+vMuMy4wb3V37XEy7zP67P2t6K147XEsu6x8KO6PC9wPgo8cD48L3A+Cgp3ZWJmb3JtxKPKvaOs1eK49sSjyr21xMu8zqy7+bSho6zKx86iyO3U2tfAw+a/qrei1tDIobXDwcvHsMv5zrTT0LXEs8m5pqOs1eLQqbPJuaajrM6iyO3Po837uLTWxrW9zfjC57+qt6LW0KOsus7OqmZvcm2jrL7Nyse0sL/av6q3oqOs1eLW1r/yvNy1xMLfvK3Kx8v5vPu8tMv5tcMmIzQzO8rCvP60psDto6zOosjtz6PN+7/J0tS9q3dlYsq1z9bOqtfAw+a/qreitcTEo8q9o6y1q8rHzfjC57+qt6K1xLv5tKHKx0hUTUy6zUhUVFDQrdLpo6zV4sG9uPayv7fWtPjAtLXEzsrM4srHSFRNTLHtz9bUqsvY09DP3qOssqLH0ta7xNy5u82ouf1mb3Jt0+u688yot/7O8cb3zajQxaGjwe3N4qOsSFRUUNCt0unO3te0zKyjrM7et6jKtc/Wz/vPorv61sajrM6qwcu94r721eLQqc7KzOKjrM6iyO20tNTswcvQwrXEv6q3osSjyr2jrNL9yOtBU1AuTkVUt/7O8cb3v9i8/qOsyrXP1sHLt+G4u7XEv9i8/qOszai5/XBvc3RiYWNru9i0q7v61sajrMq1z9bBy8rCvP7Eo9DNo6zNqLn9Y29kZWJlaGluZLy8yvXKtc/Wd2Vi0rPD5tPrQyO0+sLrtcS31sDroaPJz8r2vLzK9aOstcTIt7fHs6Ozybmmo6zSssi3yrW63LTzs8y2yMnPvPK7r8HLd2VitcS/qreio6y1q8rHy+bXxbei1bmjrLT4wLTBy87KzOKjrL7Nysd3ZWJmb3JttcS/qreiu/m0ocrH0rPD5ruvtcSjrNXi1tbLvM6sxKPKvcrHy7XE47+q0ru49tKzw+ajrMi7uvPU2tXiuPbSs8Pm0LTP7NOmysK8/qOstavKx9Xi1tbEo8q9ttTT2sa1t7Gx5LuvtcR3ZWKzzNDyyLG3psG8usO1xLi008PQ1KOssqLH0qOsx7C2y8jL1LG/qreitcS958Pmo6zN+c351Nq6z7PJyrGjrNDo0qrW2Nf2o6zV4srHzqLI7dfUvLq0tNTstcTAp8TRo6zV4srH0rvW1tLUUGFnZc6q1tDQxLXEv6q3osu8z+uhowo8YnI+CgoKTVZDxKPKvaOs1eK49sSjyr21xMu8zqy7+bSho6zKx7fWuaTH5c76o6zS1ENvbnRyb2xsZXLOqrrL0MSjrNTav6q3osqxv8nS1M/I1/Ztb2RlbNTZ1/ZDb250cm9sbGVyo6zX7rrz1/Z2aWV3o6zNqLn9yrnTw2RlbW8gdmlld8q1z9ajrNfuuvPU2czmu7vDwLmktcR2aWV3oaPV4tbWxKPKvbHks8nBy9LUyv2+3c6q1tDQxLXEv6q3osu8z+uho9futPO1xLrDtKbKx6Os1eLW1sSjyr3W0MO/uPayv7fWtry/ydLUwem77ri008OjrNfutPPP3rbItcTKtc/Wz9bU2rXEuPfW1s34wufQ6NKqo6yxyMjnu6XBqs34us3Sxravu6XBqs34oaO2+MfSo6zG5Mv7tcSx5LPJ0+/R1KOs1NrLvM/rt73D5tKyu/mxvrLJ08PV4tbWxKPKvaOs1eLW1sSjyr3X7tbVsbvKsbzk1qTD96Oss8nOqsHLserXvMu8v7y3vcq9us2/qreit73KvaGjzqLI7czhs6u1xNfAw+a7r7+qt6KjrL2lvaXNy8i0zfnI1dauueLDoqGjCjxwPjwvcD4KPHA+IDwvcD4KPGgxPjOhok1WQ7XExNqyv7n9s8w8L2gxPgo8cD4gPC9wPgo8cD4gICAgICAgICDV4rj2sr+31srHuPa3x7OjusvQxLXEzsrM4qOssb7OxLP9wcvX1Ly6wO294qOsu7m088G/0v3Tw8HLxuTL+8/gudi1xM7E1cKho7OiytS9sr3ix+Wz/k1WQ7XEu/mxvtTL16rB97PMoaM8L3A+CjxwPiA8L3A+CjxwPk1WQ7XE1vfM5bn9s8yjujwvcD4KPHA+IDwvcD4KPHA+ICAgICAgICA8aW1nIHNyYz0="/uploadfile/Collfiles/20140724/20140724094226215.jpg" alt="\">
问题:
1、 浏览器请求的地址,并不是具体的某个页面,如1234.aspx页面,而是controller/action方法,这是如何做到的?
2、 Controller被访问到以后,如何找到具体的view进行返回的?
我个人的理解就是回答了上述的问题,也就解释清楚了MVC的基本框架。
第一个问题,浏览器请求地址的问题,MVC之所以能够找到具体的Controller是因为有一个route组件,实现了路由处理的功能,将请求转化为Controller的具体方法。需要注意该组件居然是个独立组件。
Routing的作用:
1、 解析URL,识别当中的参数
2、 解析之后,调用具体的controller和action
比如首页地址是: localhost/home/index
我们发现访问上面的地址, 最后会传递给 HomeController中名为index的action(即HomeController类中的index方法).
当然服务器端不会自己去实现这个功能, 关键点就是在Global.asax.cs文件中的下列代码:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
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);
}
回来看我们的Url: localhost/home/index
localhost是域名, 所以首先要去掉域名部分: home/index
对应了上面代码中的这种URL结构:{controller}/{action}/{id}
因为我们建立了这种Url结构的识别规则, 所以能够识别出 Controller是home,action是index,id没有则为默认值"".
上述功能之所以能够实现,关键在MapRoute方法,虽然MapRoute方法是RouteCollection对象的方法,但是却被放置在System.Web.Mvc程序集中, 如果你的程序只引用了System.Web.Routing, 那么RouteCollection对象是不会有MapRoute方法的. 但是如果你同又引用了System.Web.Mvc, 则在mvc的dll中为RouteCollection对象添加了扩展方法:
public static void IgnoreRoute(this RouteCollection routes, string url);
public static void IgnoreRoute(this RouteCollection routes, string url, object constraints);
public static Route MapRoute(this RouteCollection routes, string name, string url);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults);
public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);
RouteCollection是一个集合,他的每一项应该是一个Route对象. 但是我们使用MapRoute时并没有创建这个对象, 这是因为当我们将MapRoute方法需要的参数传入时, 在方法内部会根据参数创建一个Route对象:
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
if (routes == null) {
throw new ArgumentNullException("routes");
}
if (url == null) {
throw new ArgumentNullException("url");
}
Route route = new Route(url, new MvcRouteHandler()) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints)
};
if ((namespaces != null) && (namespaces.Length > 0)) {
route.DataTokens = new RouteValueDictionary();
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}
上面就是MapRoute方法的实现, 至于在创建Route对象时第二个参数是一个MvcRouteHandler, 它是一个实现了IRouteHandler接口的类. IRouteHandler十分简单只有一个方法:
IHttpHandler GetHttpHandler(RequestContext requestContext);
参数是一个RequestContext 类实例, 这个类的结构也很简单:
public class RequestContext
{
public RequestContext(HttpContextBase httpContext, RouteData routeData);
public HttpContextBase HttpContext { get; }
public RouteData RouteData { get; }
}
其中的一个属性RouteData就包含了Routing根据Url识别出来各种参数的值, 其中就有Controller和Action的值.
归根结底, ASP.NET MVC最后还是使用HttpHandler处理请求. ASP.NET MVC定义了自己的实现了IHttpHandler接口的Handler:MvcHandler, 因为MvcRouteHandler的GetHttpHandler方法最后返回的就是MvcHandler.
MvcHandler的构造函数需要传入RequestContext 对象, 也就是传入了所有的所有需要的数据, 所以最后可以找到对应的Controller和Action, 已经各种参数.
(引用参考:https://www.cnblogs.com/zhangziqiu/archive/2009/02/28/ASPNET-MVC-2.html)
(引用参考:https://www.cnblogs.com/zhangziqiu/archive/2009/03/11/Aspnet-MVC-3.html)
第二个问题:Controller找到了,Action也找到了,此时如何哪?
下面分层次的总结Controller处理流程:
1. 页面处理流程
发送请求 –> UrlRoutingModule捕获请求 –>MvcRouteHandler.GetHttpHandler() –> MvcHandler.ProcessRequest()
2.MvcHandler.ProcessRequest() 处理流程:
使用工厂方法获取具体的Controller –> Controller.Execute() –> 释放Controller对象
3.Controller.Execute() 处理流程
获取Action –> 调用Action方法获取返回的ActionResult –> 调用ActionResult.ExecuteResult() 方法
4.ActionResult.ExecuteResult() 处理流程
获取IView对象-> 根据IView对象中的页面路径获取Page类-> 调用IView.RenderView() 方法(内部调用Page.RenderView方法)
通过对MVC源代码的分析,我们了解到Controller对象的职责是传递数据,获取View对象(实现了IView接口的类),通知View对象显示.View对象的作用是显示.虽然显示的方法RenderView()是由Controller调用的,但是Controller仅仅是一个"指挥官"的作用, 具体的显示逻辑仍然在View对象中.需要注意IView接口与具体的ViewPage之间的联系.在Controller和View之间还存在着IView对象.对于ASP.NET程序提供了WebFormView对象实现了IView接口.WebFormView负责根据虚拟目录获取具体的Page类,然后调用Page.RenderView().
引用参考:(Http://www.cnblogs.com/zhangziqiu/archive/2009/03/11/Aspnet-MVC-3.html)
讲到这里,相信很多人开始似乎明白了,又似乎不明白了,下面我做进一步的讲解,先看一下,通常Controller的实现如下:
public class HomeController:Controller
{
public ActionResult Index()
{
Return View(“Index”);
}
}
先看看关键类ActionResult,这个返回值,体现了微软精心设计,为什么做这么个类,其实本质而言,微软希望这个action可以返回更多内容,而不仅仅是view。
类名 |
抽象类 |
父类 |
功能 |
ContentResult |
|
|
根据内容的类型和编码,数据内容. |
EmptyResult |
|
|
空方法. |
FileResult |
abstract |
|
写入文件内容,具体的写入方式在派生类中. |
FileContentResult |
|
FileResult |
通过文件byte[] 写入文件. |
FilePathResult |
|
FileResult |
通过文件路径写入文件. |
FileStreamResult |
|
FileResult |
通过文件Stream 写入文件. |
HttpUnauthorizedResult |
|
|
抛出401错误 |
JavaScriptResult |
|
|
返回javascript文件 |
JsonResult |
|
|
返回Json格式的数据 |
RedirectResult |
|
|
使用Response.Redirect重定向页面 |
RedirectToRouteResult |
|
|
根据Route规则重定向页面 |
ViewResultBase |
abstract |
|
调用IView.Render() |
PartialViewResult |
|
ViewResultBase |
调用父类ViewResultBase
的ExecuteResult方法. |
ViewResult |
|
ViewResultBase |
调用父类ViewResultBase
的ExecuteResult方法. |
这里我们主要讲解viewResult的作用。
在ASP.NETMVC中,ViewResult用的最多,Controller有一个View方法,它来实例化一个ViewResult对象,并返回。
下面是View方法:
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, TempData = TempData }; }
ViewResult类的ExecuteResult方法的具体参考如下:
public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (String.IsNullOrEmpty(ViewName)) { ViewName = context.RouteData.GetRequiredString("action"); } ViewEngineResult result = null; if (View == null) { result = FindView(context); // 很关键,找到具体的view View = result.View; } ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);
// 很关键,渲染自己 View.Render(viewContext, context.HttpContext.Response.Output);
if (result != null) { result.ViewEngine.ReleaseView(context, View); } }
那么如何FindView哪?具体如下:
rotectedoverrideViewEngineResult FindView(ControllerContext context) {
ViewEngineResult result =ViewEngineCollection.FindView(context, ViewName, MasterName);
if (result.View !=
null) {
return result;
}
//we need to generate an exception containing all the locations we searched
StringBuilder locationsText =
new StringBuilder();
foreach (string location
in result.SearchedLocations) {
locationsText.AppendLine();
locationsText.Append(location);
}
throw
new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
MvcResources.Common_ViewNotFound,ViewName, locationsText));
}
从ViewResult类的FindView方法中,得知ViewEngineResult是通过ViewEngineCollection的FindView得到的,而ViewEngineCollection正是ViewEngines的静态属性Engines,Engines返回一个只有一个WebFormViewEngine类型实例的一个集合。所以,ViewEngineResult会是调用WebFormViewEngine类的FindView方法返回的结果。如果ViewEngins的静态属性Engines有多个ViewEngine提供,那么就依次遍历它们直到找到第一个不为空的ViewEngineResult为止。这样我们就可以在同一个MVC网站中使用多种视图引擎了。
静态类ViewEngines的描述如下:
public static class ViewEngines
{
private static readonly ViewEngineCollection _engines = new ViewEngineCollection { new WebFormViewEngine(), new RazorViewEngine() };
public static ViewEngineCollection Engines
{
get { return _engines;}
}
}
public class ViewEngineCollection : Collection
{
//其他成员
public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName);
public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName);
}
从上述例子可以看出,起始微软为我们提供了两个ViewEngine, WebFormViewEngine和RazorViewEngine,WebFormViewEngine对应的是ASPX界面,RazorViewEngine对应的是.cshtml/.vbhtml引擎
此外,这里有一个隐藏很深的概念,似乎很多书都没讲清楚,每一个引擎都会对应一个view,作为页面渲染使用,对于viewengine做进一步的解释,就是说,为什么一个view当中的之类的界面元素最后可以变成html,就是这些引擎在起作用,他们的内部实现,可以提供类似正则表达式或者是页面parsing的方法,完成页面字符串解析,从而转换为对应的html,再response给客户端浏览器。
微软提供的两种viewengine和view如下:
RazorViewEngine和Razorview
(参考引用:https://www.cnblogs.com/artech/archive/2012/09/05/razor-view-engine-02.html)
WebformViewengine和Webformview
通过上边的讲述,基本的概念已经讲清楚了,如果希望实现自己的viewengine,可以查看一下微软参考实现是如何做到的,然后我们就可以防治自己的viewengine了。这里便需要进一步说明的是,为什么MVC需要viewengine,而WEBFORM不需要,是因为,微软的webform是直接通过IHttphandler处理了,也就是说ASP.NETWEBFORM模式中的HTTPHANDLER完成了事件处理和页面显示的双重功能。而ASP.NETMVC没有事件处理,因此页面显示被划到了viewengine当中实现了。事件处理,被controller替换处理了。
微软的Razorview和Webformview本质上是实现于IView接口,而WebformViewengine和Webformview本质上是实现于IViewEngine
下面介绍他们的实现逻辑:
public interface IView
2: {
3: void Render(ViewContext viewContext, TextWriter writer);
4: }
5:
6: public class ViewContext : ControllerContext
7: {
8: //其他成员
9: public virtual bool ClientValidationEnabled { get; set; }
10: public virtual bool UnobtrusiveJavaScriptEnabled { get; set; }
11:
12: public virtual TempDataDictionary TempData { get; set; }
13: [Dynamic]
14: public object ViewBag { [return: Dynamic] get; }
15: public virtual ViewDataDictionary ViewData { get; set; }
16: public virtual IView View { get; set; }
17: public virtual TextWriter Writer { get; set; }
18: }
19:
20: public abstract class HttpResponseBase
21: {
22: //其他成员
23: public virtual TextWriter Output { get; set; }
24: }
1: public interface IViewEngine
2: {
3: ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache);
4: ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache);
5: void ReleaseView(ControllerContext controllerContext, IView view);
6: }
1: public class ViewEngineResult
2: {
3: public ViewEngineResult(IEnumerable searchedLocations);
4: public ViewEngineResult(IView view, IViewEngine viewEngine);
5:
6: public IEnumerable SearchedLocations { get; }
7: public IView View { get; }
8: public IViewEngine ViewEngine { get; }
9: }
1: public class ViewResult : ViewResultBase
2: {
3: protected override ViewEngineResult FindView(ControllerContext context);
4: public string MasterName { get; set; }
5: }
6:
7: public abstract class ViewResultBase : ActionResult
8: {
9: public override void ExecuteResult(ControllerContext context);
10: protected abstract ViewEngineResult FindView(ControllerContext context);
11:
12: public object Model { get; }
13: public TempDataDictionary TempData { get; set; }
14: [Dynamic]
15: public object ViewBag { [return: Dynamic] get; }
16: public ViewDataDictionary ViewData { get; set; }
17: public string ViewName { get; set; }
18: public ViewEngineCollection ViewEngineCollection { get; set; }
19: public IView View { get; set; }
20: }
(参考引用:https://www.cnblogs.com/artech/archive/2012/08/22/view-engine-01.html)
自定义的View以及相关的Viewengine,参考如下:
public class StaticFileView:IView
2: {
3: public string FileName { get; private set; }
4: public StaticFileView(string fileName)
5: {
6: this.FileName = fileName;
7: }
8: public void Render(ViewContext viewContext, TextWriter writer)
9: {
10: byte[] buffer;
11: using (FileStream fs = new FileStream(this.FileName, FileMode.Open))
12: {
13: buffer = new byte[fs.Length];
14: fs.Read(buffer, 0, buffer.Length);
15: }
16: writer.Write(Encoding.UTF8.GetString(buffer));
17: }
18: }
internal class ViewEngineResultCacheKey
2: {
3: public string ControllerName { get; private set; }
4: public string ViewName { get; private set; }
5:
6: public ViewEngineResultCacheKey(string controllerName, string viewName)
7: {
8: this.ControllerName = controllerName ?? string.Empty;
9: this.ViewName = viewName ?? string.Empty;
10: }
11: public override int GetHashCode()
12: {
13: return this.ControllerName.ToLower().GetHashCode() ^ this.ViewName.ToLower().GetHashCode();
14: }
15:
16: public override bool Equals(object obj)
17: {
18: ViewEngineResultCacheKey key = obj as ViewEngineResultCacheKey;
19: if (null == key)
20: {
21: return false;
22: }
23: return key.GetHashCode() == this.GetHashCode();
24: }
25: }
1: public class StaticFileViewEngine : IViewEngine
2: {
3: private Dictionary viewEngineResults = new Dictionary();
4: private object syncHelper = new object();
5: public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
6: {
7: return this.FindView(controllerContext, partialViewName, null, useCache);
8: }
9:
10: public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
11: {
12: string controllerName = controllerContext.RouteData.GetRequiredString("controller");
13: ViewEngineResultCacheKey key = new ViewEngineResultCacheKey(controllerName, viewName);
14: ViewEngineResult result;
15: if (!useCache)
16: {
17: result = InternalFindView(controllerContext, viewName, controllerName);
18: viewEngineResults[key] = result;
19: return result;
20: }
21: if(viewEngineResults.TryGetValue(key, out result))
22: {
23: return result;
24: }
25: lock (syncHelper)
26: {
27: if (viewEngineResults.TryGetValue(key, out result))
28: {
29: return result;
30: }
31:
32: result = InternalFindView(controllerContext, viewName, controllerName);
33: viewEngineResults[key] = result;
34: return result;
35: }
36: }
37:
38: private ViewEngineResult InternalFindView(ControllerContext controllerContext, string viewName, string controllerName)
39: {
40: string[] searchLocations = new string[]
41: {
42: string.Format( "~/views/{0}/{1}.shtml", controllerName, viewName),
43: string.Format( "~/views/Shared/{0}.shtml", viewName)
44: };
45:
46: string fileName = controllerContext.HttpContext.Request.MapPath(searchLocations[0]);
47: if (File.Exists(fileName))
48: {
49: return new ViewEngineResult(new StaticFileView(fileName), this);
50: }
51: fileName = string.Format(@"\views\Shared\{0}.shtml", viewName);
52: if (File.Exists(fileName))
53: {
54: return new ViewEngineResult(new StaticFileView(fileName), this);
55: }
56: return new ViewEngineResult(searchLocations);
57: }
58:
59: public void ReleaseView(ControllerContext controllerContext, IView view)
60: { }
61: }
1: public class MvcApplication : System.Web.HttpApplication
2: {
3: protected void Application_Start()
4: {
5: //其他操作
6: ViewEngines.Engines.Insert(0, new StaticFileViewEngine());
7: }
8: }
1: public class HomeController : Controller
2: {
3: public ActionResult ShowNonExistentView()
4: {
5: return View("NonExistentView");
6: }
7:
8: public ActionResult ShowStaticFileView()
9: {
10: return View();
11: }
12: }
我们为Action方法ShowStaticFileView创建一个StaticFileView类型的View文件ShowStaticFileView.shtml(该View文件保存在“~/Views/Home”目录下,扩展名不是.cshtml,而是shtml),其内容就是如下一段完整的HTML。
1:
2:
3:
4: Static File View
5:
6:
7: 这是一个自定义的StaticFileView!
8:
9:
(参考引用:https://www.cnblogs.com/artech/archive/2012/08/23/view-engine-02.html)
喎?>上一篇: python实现简单多人聊天室
下一篇: Hibernate学习笔记
推荐阅读
-
解决Asp.net Mvc返回JsonResult中DateTime类型数据格式问题的方法
-
ASP.Net MVC_DotNetZip简单使用方法,解决文件压缩的问题
-
ASP.NET MVC+EF在服务端分页使用jqGrid以及jquery Datatables的注意事项
-
基于ASP.NET MVC的ABP框架入门学习教程
-
ASP.NET MVC的Localization本地化多语言支持
-
详解Asp.Net MVC的Bundle捆绑
-
解读ASP.NET 5 & MVC6系列教程(12):基于Lamda表达式的强类型Routing实现
-
解读ASP.NET 5 & MVC6系列教程(17):MVC中的其他新特性
-
asp.net mvc发送邮件实例讲解
-
ASP.NET MVC中使用jQuery时的浏览器缓存问题详解