欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ASP.NET MVC的流程讲解

程序员文章站 2022-05-11 12:34:49
开始想这个标题的时候,很是忧郁到底叫什么标题会比较好哪,最后还是确定为讲解,虽然略显初级,但是很多概念,想通了还是比较有价值的。废话略过,开始!...

开始想这个标题的时候,很是忧郁到底叫什么标题会比较好哪,最后还是确定为讲解,虽然略显初级,但是很多概念,想通了还是比较有价值的。废话略过,开始!

1、MVC的基本开发流程

2、webform和MVC的选择

3、MVC的内部过程

1、MVC的开发流程

MVC的出现时微软在2009年左右开始提出的网站开发的新的发展方向,这个方面的开发官方解释是可以比较好的实现三层分离,而且分离之后,可以实现复用等相关好处,通常人们列举的例子是ui方面可以同时支持HTML网络或者WAP网络。但是其实个人的理解是,动态网站的开发经过不断地证实和发展,java的struts模型,可以提高开发速度,也可以降低差多,是比较好的框架,而微软也需要提供自己的开发框架。不能够只是一个界面一个界面的设计方式,设计模式逐步进入到了web开发的领域。

MVC使用vs2010进行开发时(这里介绍的是MVC2),首先需要选在一个模板,然后vs2010会帮忙创建好对应的目录结构。


ASP.NET MVC的流程讲解

喎? 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方法.
重写了父类的FindView方法.
寻找用户控件.ascx文件

ViewResult

ViewResultBase

调用父类ViewResultBase 的ExecuteResult方法.
重写了父类的FindView方法.
寻找页面.aspx文件

这里我们主要讲解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)

喎?>