MVC3入门教程
p.Net?MVC已經到第三版了,相信大家也都熟悉了,我也不再重復相關概念性的東西了。但是大家一定要了解,Asp.Net?MVC是微軟的一個開源的UI層框架,是AspNet的另外一種開發模式。好廢話不多說,那我們開始進入Asp.Net?MVC3?的學習中來,工欲善其事,必先利其器!所以我們必須搭建好自己的開發環境才能為我們下一步的學習和開發提供更好的支持。
那你的機器的必備條件是:
1)VS2010(當然你非要使用VS2008那我也不能說什么了)
2)SqlServer?2000/2005/2008
3)Asp.Net?MVC3安裝包(應該是需要VS2010SP1)
?
下面提供一些URL鏈接方便大家下載學習:
1)Asp.Net?MVC3的官網:http://www.asp.net/mvc/mvc3?
如下圖所示:
這個大家在安裝過程中可能時間會非常長(>=20分鐘),大家忍耐一下!
2)安裝Asp.Net?MVC3安裝包
http://www.microsoft.com/web/gallery/install.aspx?appid=MVC3?
3)Asp.Net?MVC3源碼開源地址:
http://aspnet.codeplex.com/releases/view/58781?
如下圖所示:
第二節:第一個Asp.Net?MVC3項目
1)創建項目:
2)選擇項目的默認視圖引擎
我們選擇一個Empty模板,然后選擇Razor視圖引擎(Asp.Net?MVC3中提供的新的視圖引擎)選擇HTML5標記支持打上勾(這塊我也不了解,呵呵,希望做過這塊研究的高手跟我分享一下!)。
3)創建后的項目:
?
Asp.Net?MVC3貌似跟之前的版本創建的項目模板沒什么大的不同,文件夾也基本相似。當然我們看到文件夾內的Jquery的包更新到了1.5.1?。后面的文章中會對每個文件夾都做相關的介紹。在此就不多啰嗦了。
4)添加代碼,跑起來我們的第一個Demo
首先:在Controller文件夾上右擊,選擇添加菜單,然后選擇Controller,如下圖所示:
然后彈出對話框,將Controller命名為HomeController,注意后面的Controller不要去掉,不然它就不會被識別為Controller了,最后點擊添加。如下圖所示:
設計器自動為我們生成代碼如下:
?
using?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Web;
using?System.Web.Mvc;
namespace?MvcApplication1.Controllers
{
public?class?HomeController?:?Controller
{
//
//?GET:?/Home/
public?ActionResult?Index()
{
return?View();
}
}
}
下一步我們在Action上添加我們需要的視圖,如下所示:
5)在前臺頁面添加我們自己的Html標簽
@{
ViewBag.Title?=?"Index";
}
<h2>Index</h2>
<h1>Hello!?It's?my?first?Asp.Net?MVC?Web!</h1>
注意:紅色為我們自己添加的部分
6)最終結果:
在上一篇文章Asp.Net?MVC3?簡單教程(一)環境準備?中我簡單介紹了Asp.Net?MVC3項目的安裝和第一個Asp.Net?MVC3項目的基本情況。沒有詳細介紹項目中各個文件夾的作用,以及創建的第一個頁面是怎樣運行起來的?還有好多的疑問,那在這篇文章中我們將詳細介紹項目中各個文件夾的作用,并真的第一個項目我們簡要介紹一下Asp.Net?MVC的URL驅動的是怎么回事。?
第一節:Asp.Net?MVC3項目介紹
讓我們先看一下,一個普通的Asp.Net?MVC3項目的樣例,如下圖所示?
???
跟WebFrom還是有區別的,如果你已經了解Asp.Net?MVC2的話,那就感覺異常熟悉了!但還是有些區別的。不管怎樣我們都一一介紹一下。?
很有意思的事情是即使我們創建一個空的MVC項目,VS也自動幫我們創建以上圖所示的目錄,這是為何呢?這是由于MVC秉承了“約定大于配置”的思想,我們在使用Asp.Net?MVC3開發項目時也要注意,一定要按照它的約定辦事,比如:Controller在返回Action后需要一個View進行展示(當然是調用了View()方法時),這時候Asp.Net?MVC回到Views文件夾下找到Controller名字相同的文件夾下面找到具體的頁面進行渲染,當然如果找不到會去Shared文件夾下去找。看下表所示的就是Asp.Net?MVC3中各個文件夾的作用。?
| 文件夾 | 作用 |
| /Controllers | 存放控制器類【職責是:處理用戶的請求,指揮具體的頁面進行渲染交給客戶端】 |
| /Views | 存放各個控制器對應的視圖文件,如果是Razor引擎的話那后綴是cshtml.如果使用的WebFrom的視圖引擎的話,那還是Aspx后綴。 |
| /Content | 主要存放照片、CSS、Flash等文件 |
| /Scripts | 主要存放腳本文件【微軟默認給我們提供了JQuery1.5.1的包,看來JQuery已經成為默認的工業標準了!我們沒有退路了,呵呵,當然我個人也非常喜歡JQuery】 |
| /Models | 主要存放ViewModel類【當然這個不是嚴格這樣要求的,而是推薦你這么做。】 |
其他的幾個比較有意思的文件:
一個是Web.Config,另外一個是Global.asax雖然我們大家都非常熟悉,但是跟之前我們WebFrom還是有很多的區別的。WebConfig文件中,配置了啟用客戶端腳本驗證、配置了System.Web.Routing、System.Web.Mvc等組件。而Global.asax則在應用啟動的時候注冊了全局的Area【區域,后面會相信講解】、全局Filter、路由等。
第二節:Asp.Net?MVC的請求處理模型
在上一篇中我們也簡單做了個小例子,直接添加一個Controller,然后在Action上添加一個View,直接運行,然后就在我們面前呈現了一個普通的Html頁面。那我們詳細解釋一下這種開發方式或者說開發模型。在講解之前我們先認識幾個概念:
Controller:控制器。在Contrller文件夾添加的以Controller結尾的類就是控制器,它的每個方法就是一個Action。它的職責是從Model中獲取數據,并將數據交給View,它是個指揮家的角色,它并不控制View的顯示邏輯,只是將Model的數據交給View,而具體的怎樣展示數據那是View的職責,所以Controller跟View是一個弱耦合的狀態,而且Controller可以任意指定具體的View進行渲染。所以達到了UI層的代碼和實體良好的分離。
View:視圖.負責數據的展示,當然這個視圖代碼的編寫應該是更接近純凈的Html的,而View層代碼的書寫又直接跟視圖引擎解析的規則有關,所以Razor的語法跟webFrom視圖引擎的語法截然不同。而筆者更傾向更喜歡Razor語法的簡潔、方便。
Model:很多人把Model理解成領域模型,而MVC本身是一個表現模式,它是更傾向于UI層的一個框架,所以一般我們指定的Model呢在使用時一般作為ViewModel來用,但是總的MVC的思想呢,Model還是領域相關的東西吧。
經過MVC3個模塊的了解分析,我們大體也知道了Asp.Net?MVC的一些基本的概念。接下來我們分析一個完整的Http的處理過程。看下面一個圖:
?
客戶端發送一個Http請求,首先被我們的IIS捕獲到,然后根據Url請求的格式,最終交給我們的Route組件,然后它負責解析出我們的Url具體請求的是哪個Controller下的哪個Action。然后MVC經過處理調用我們的Action執行。在Action中我們一般會從業務的Fa?ade層取出數據,然后將傳輸層的數據轉換成ViewModel再交給View的視圖引擎渲染,最終生成Html的字節流寫回客戶端。
回到我們第一個項目中的情況是,請求:Http://localhost/Home/Index請求過來,由Route組件解析出Controller是Home,Action是Index,則通過工廠創建一個Controller的實例,然后調用InvokeAction方法,執行Index的方法,最終執行View()方法返回一個ViewResult實例,再調用自己的ExcuteResult方法,將數據上下文和輸出流交給視圖引擎,然后最終渲染成Html頁面交給客戶端,最終就看到了我們的第一個頁面。
總結一下:
Asp.Net?MVC所有的請求都歸結到Action上,而且Asp.Net?MVC請求--處理--響應的模型非常清晰,而且沒有WebFrom那種復雜的生命周期,整個請求處理非常明晰簡單,又回歸到了最原始的Web開發方式,就是簡單的請求處理響應!
前言
前面兩篇寫的比較簡單,剛開始寫這個系列的時候我面向的對象是剛開始接觸Asp.Net?MVC的朋友,所以寫的盡量簡單。所以寫的沒多少技術含量。把這些技術總結出來,然后一簡單的方式讓更多的人很好的接受這是我一直努力的方向。后面會有稍微復雜點的項目!讓我們一起期待吧!
此文我將跟大家介紹一下Asp.Net?MVC3?Filter的一些用法。你會了解和學習到全局Fileter,Action?Filter等常用用法。
第一節:Filter知識儲備
項目大一點總會有相關的AOP面向切面的組件,而MVC(特指:Asp.Net?MVC,以下皆同)項目中呢Action在執行前或者執行后我們想做一些特殊的操作(比如身份驗證,日志,異常,行為截取等),而不想讓MVC開發人員去關心和寫這部分重復的代碼,那我們可以通過AOP截取實現,而在MVC項目中我們就可以直接使用它提供的Filter的特性幫我們解決,不用自己實現復雜的AOP了。
Asp.Net?MVC提供了以下幾種默認的Filter:
| Filter?Type | 實現接口 | 執行時間 | Default?Implementation |
| Authorization?filter | IAuthorizationFilter | 在所有Filter和Action執行之前執行 | AuthorizeAttribute |
| Action?filter | IActionFilter | 分別在Action執行之前和之后執行。 | ActionFilterAttribute |
| Result?filter | IResultFilter | 分別在Action?Result執行之后和之前 | ResultFilterAttribute |
| Exception?filter | IExceptionFilter | 只有在filter, 或者?action?method,?或者?action?result?拋出一個異常時候執行 ? | HandleErrorAttribute |
大家注意一點,Asp.Net?MVC提供的ActionFilterAttribute默認實現了IActionFilter和IResultFilter。而ActionFilterAttribute是一個Abstract的類型,所以不能直接使用,因為它不能實例化,所以我們想使用它必須繼承一下它然后才能使用,下圖所示的是ActionFilterAttribute的實現:
所以我們在實現了ActionFilterAttribute,然后就可以直接重寫一下父類的方法如下:
public?virtual?void?OnActionExecuted(ActionExecutedContext?filterContext);//在Action執行之后執行?
public?virtual?void?OnActionExecuting(ActionExecutingContext?filterContext);?//在Action執行前執行
public?virtual?void?OnResultExecuted(ResultExecutedContext?filterContext);//在Result執行之后?
public?virtual?void?OnResultExecuting(ResultExecutingContext?filterContext);?//在Result執行之前
然后我們就可以直接在Action、Result執行之前之后分別做一些操作。
第二節:Action?Filter實戰
光說不練假把式,那現在我們就直接做一個例子來實際演示一下。
首先我們添加一個普通的類,直接上代碼吧:
public?class?DemoActionAttributeFilter?:?ActionFilterAttribute
{
public?string?Message?{?get;?set;?}
public?override?void?OnActionExecuted(ActionExecutedContext?filterContext)
{?//在Action執行之后執行?輸出到輸出流中文字:After?Action?Excute?xxx
filterContext.HttpContext.Response.Write(@"<br?/>After?Action?Excute"?+?"\t?"?+?Message);
base.OnActionExecuted(filterContext);
}
public?override?void?OnActionExecuting(ActionExecutingContext?filterContext)
{?//在Action執行前執行
filterContext.HttpContext.Response.Write(@"<br?/>Before?Action?Excute"?+?"\t?"?+?Message);
base.OnActionExecuting(filterContext);
}
public?override?void?OnResultExecuted(ResultExecutedContext?filterContext)
{?//在Result執行之后?
filterContext.HttpContext.Response.Write(@"<br?/>After?ViewResult?Excute"?+?"\t?"?+?Message);
base.OnResultExecuted(filterContext);
}
public?override?void?OnResultExecuting(ResultExecutingContext?filterContext)
{?//在Result執行之前
filterContext.HttpContext.Response.Write(@"<br?/>Before?ViewResult?Excute"?+?"\t?"?+?Message);
base.OnResultExecuting(filterContext);
}
}
寫完這個代碼后,我們回到Action上,打上上面的標記如下所示:
?
[DemoActionAttributeFilter(Message?=?"action")]
public?ActionResult?Index()
{?//Action?執行時往輸出流寫點代碼
this.ControllerContext.HttpContext.Response.Write(@"<br?/>Action?Excute");
return?Content("Result?Excut!?");
}
然后執行F5,頁面上則會顯示為:
最終我們看到了在Action執行之前和之后都執行了我們的重寫的DemoActionAttributeFilter方法,Result執行前后也執行了我們的Filter的方法。
總的執行順序是:
Action執行前:OnActionExecuting方法先執行→Action執行→OnActionExecuted方法執行→OnResultExecuting方法執行→返回的ActionRsult中的ExcuteResult方法執行→OnResultExecuted執行。最終顯示的效果就是如上圖所示。
感覺很爽吧!呵呵!
如果我們將此標簽打到Controller上的話,DemoActionAttributeFilter將作用到Controller下的所有的Action。例如如下代碼所示:
[DemoActionAttributeFilter(Message?=?"controller")]
public?class?HomeController?:?Controller
{
[DemoActionAttributeFilter(Message?=?"action")]
public?ActionResult?Index()
{
this.ControllerContext.HttpContext.Response.Write(@"<br?/>Action?Excute");
return?Content("<br/>Result?Excut!?");
}
}
?那就有個問題了我們再執行顯示的頁面會有什么情況呢?Controller上的Filter會執行嗎?那標簽的作用會執行兩次嗎?下面是最后的執行結果如下圖所示:
結果說明:默認情況下Action上打了DemoActionAttributeFilter?標簽后,雖然在Controller上也打上了此標簽,但它只有Action上的標簽起作用了。Index?執行時,Filter的方法只執行了一次,而某些情況下我們也想讓Controller上的FilterAttribute也執行一次DemoActionAttributeFilter?
那我們怎么才能讓Controller上的[DemoActionAttributeFilter(Message?=?"controller")]也起作用呢?
答案是:我們只需在DemoActionAttributeFilter類的定義上打上標記[AttributeUsage(AttributeTargets.All,?AllowMultiple?=?true)]即可【下面類的最上面紅色字體部分】,也就是讓其成為可以多次執行的Action。代碼如下:?
[AttributeUsage(AttributeTargets.All,?AllowMultiple?=?true)]
public?class?DemoActionAttributeFilter?:?ActionFilterAttribute
{
public?string?Message?{?get;?set;?}
public?override?void?OnActionExecuted(ActionExecutedContext?filterContext)
{
filterContext.HttpContext.Response.Write(@"<br?/>After?Action?Excute"?+?"\t?"?+?Message);
base.OnActionExecuted(filterContext);
}
public?override?void?OnActionExecuting(ActionExecutingContext?filterContext)
{
filterContext.HttpContext.Response.Write(@"<br?/>Before?Action?Excute"?+?"\t?"?+?Message);
base.OnActionExecuting(filterContext);
}
public?override?void?OnResultExecuted(ResultExecutedContext?filterContext)
{
filterContext.HttpContext.Response.Write(@"<br?/>After?ViewResult?Excute"?+?"\t?"?+?Message);
base.OnResultExecuted(filterContext);
}
public?override?void?OnResultExecuting(ResultExecutingContext?filterContext)
{
filterContext.HttpContext.Response.Write(@"<br?/>Before?ViewResult?Excute"?+?"\t?"?+?Message);
base.OnResultExecuting(filterContext);
}
}
然后我們執行的效果如圖所示:
我們看到的結果是Controller上的ActionFilter先于Action上打的標記執行。同樣Result執行ExcuteResult方法之前也是先執行Controller上的Filter標記中的OnResultExcuteing方法。
最后的執行順序是:Controller上的OnActionExecuting→Action上的OnActionExecuting→Action執行→Action上的OnActionExecuted→Controller上的OnActionExecuted?
到此Action就執行完畢了,我們看到是一個入棧出棧的順序。后面是Action返回ActionResult后執行了ExecuteResult方法,但在執行之前要執行Filter。具體順序為:
接上面→Controller的OnResultExecuting方法→Action上的OnResultExecuting→Action返回ActionResult后執行了ExecuteResult方法→Action上的OnResultExecuted執行→Controller上的OnResultExecuted執行→結束
第三節:Gloable?Filter實戰
又接著一個問題也來了,我們想有些公共的方法需要每個Action都執行以下,而在所有的Controller打標記是很痛苦的。幸好Asp。Net?MVC3帶來了一個美好的東西,全局Filter。而怎么注冊全局Filter呢?答案就在Global.asax中。讓我們看以下代碼,我是如何將上面我們定義的DemoActionAttributeFilter?注冊到全局Filter中。上代碼:?
public?class?MvcApplication?:?System.Web.HttpApplication
{
public?static?void?RegisterGlobalFilters(GlobalFilterCollection?filters)
{
filters.Add(new?HandleErrorAttribute());
}
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?=?UrlParameter.Optional?}?//?Parameter?defaults
);
}
protected?void?Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new?DemoActionAttributeFilter()?{?Message?=?"Gloable"?});
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
跟普通的MVC2.0中的Global.asax的區別就是紅色部分的代碼,我們看到代碼中我將自己定義的DemoActionAttributeFilter的實例加入到GlobalFilters.Filters集合中,然后下面一句就是注冊全局Filter:RegisterGlobalFilters(GlobalFilters.Filters);
這樣我們所有的Action和Result執行前后都會調用我們的DemoActionAttributeFilter的重寫的方法。
再次運行我們的demo看到的結果是:
我們看到的結果是全局的Action首先執行,然后才是Controller下的Filter執行,最后才是Action上的標簽執行。當然這是在DemoActionAttributeFilter類的定義上打上標記[AttributeUsage(AttributeTargets.All,?AllowMultiple?=?true)]的前提下。不然?如果Action打上了標簽跟Controller的相同則它只會執行Action上的Filter。
總結
經過這一篇文章的介紹我們大體了解了Filter的使用方法,還了解到全局Filter的用法,尤其是當相同的Filter重復作用到同一個Action上時,如果沒有設置可多次執行的標簽那只有Action上的Filter執行,而Controller和全局Filter都被屏蔽掉,但是設置可多次執行,那首先執行全局Filter其次是Controller再次之就是Action上的Filter了。
今天寫點關于Asp.Net?MVC的PipeLine。首先我們確認一點,Asp.Net?WebFrom和Asp.Net?MVC是在.Net平臺下的兩種web開發方式。其實他們都是基于Asp.Net?Core的不同表現而已。看下面一張圖,我們就能理解了WebForm和Asp.Net?MVC的一個關系了。
那好我們了解了Asp.Net平臺下的兩種開發方式,相信大家對于WebForm的Pipeline都非常熟悉了,當然這也是你熟悉Asp.Net開發的必經之路。而看了很多關于Asp.Net?MVC的資料很少有把整個Pipeline講的非常清楚的。我暫時將自己淺陋的整理和理解總結如下,歡迎高手拍磚!
?
第一階段:客戶端請求
客戶端通過瀏覽器、其他軟件、自己編寫WebClinet、模擬HttpRequest等方法來請求一個URL。當然在Asp.Net?WebFrom下,所有的請求都是歸結到Handler上,普通的Aspx、Ascx等都是繼承自IHttpHandler接口的一些實例,所以我總結出來:WebFrom下所有的請求都是請求的Handler【不考慮Url重寫】。而做Asp.Net?MVC的項目呢,所有的請求是都歸結到Action上,Url應該是直接請求Action。
第二階段:IIS?Web服務器
當一個請求到達IIS服務器后,Windows系統的內核模塊?HTTP.SYS就能監聽到此次請求,并將此次請求的URL、IP以及端口等信息解析出來并將此請求交給注冊的應用來處理:也就是IIS的站點。請求此時就到達了IIS,IIS【此處僅代表IIS6.0版本】就會去檢查此次請求的URL的后綴并將相應的請求交給配置的處理后綴相應的isapi。如果是.aspx或者ascx等直接交給默認設置了此處理項的AspNet_isapi.dll來處理,如果我們想處理Asp.Net?MVC的請求的話,我們需要在IIS里面設置處理*.*請求交給AspNet_isapi.dll來處理,才能將一個普通的MVC請求的URL:Http://localhost/DemoController/DemoAction交給AspNet_Isapi.dll來處理。
第三階段:Asp.Net?運行時
此時請求到AspNet_Isapi.dll后,它負責啟動Asp.Net?RunTime【如過啟動了,直接將請求交給RunTime】。Asp.Net?運行時【HttpRuntime】此時會初始化一下HttpContext上下文,并從HttpApplicationFactory去創建一個HttpApplication對象,并將HttpContext賦值給HttpApplication,此后HttpContext的信息就會一直在管道內往下傳遞。
HttpApplication對象開始初始化WebConfig文件中注冊的IHttpModule,請求帶著請求信息【HttpContext】隨著管道流過多個HttpModule【一般可以做為權限校驗、行為記錄、日志等等,就是在到達Handler之前我們都可以直接處理此次Http請求,甚至可以重寫URL】,當然也會經過我們注冊的一些自定義的IHttpModule,在.Net?4.0的machine?的config文件中默認配置了一個URLRouteModule,這個也就是我們普通的Asp.Net?MVC項目中的路由DLL引用【System.Web.Routing】內部的一個實現了IHttpModule接口的實例類。請求最終流向了路由組件。
第四階段:Routing組件
如果你用的是MVC?2+?.NET?3.5,則你會在你的web項目中發現UrlRoutingModule就配置在你的Web.Config。.NET?4卻是在.Net的默認配置文件中配置的。
UrlRoutingModule做了這么幾個工作:首先他會拿著你的請求到路由表中去匹配相應的路由規則。而路由表規則的定義是在HttpApplication初始化的時候由靜態方法執行的,且看一個普通的Asp.Net?MVC項目的Global.asax
public?class?MvcApplication?:?System.Web.HttpApplication
????{
????????public?static?void?RegisterGlobalFilters(GlobalFilterCollection?filters)
????????{
????????????filters.Add(new?HandleErrorAttribute());
????????}
????????public?static?void?RegisterRoutes(RouteCollection?routes)//定義路由表規則
????????{
????????????routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
????????????routes.MapRoute(
????????????????"Default",?//?路由名稱
????????????????"{controller}/{action}/{id}",?//?帶有參數的?URL
????????????????new?{?controller?=?"Home",?action?=?"Index",?id?=?UrlParameter.Optional?}?//參數默認值
????????????);
????????}
????????protected?void?Application_Start()
????????{
????????????AreaRegistration.RegisterAllAreas();
????????????RegisterGlobalFilters(GlobalFilters.Filters);
????????????RegisterRoutes(RouteTable.Routes);//注冊路由表
????????}
????}
而路由表的規則的注冊是在?Application_Start()?方法內部,那此時請求在URLRouteModule內部到路由表中的所有規則進行匹配,并把匹配的Controller的信息和Action的信息以及RouteData等信息都解析處理,然后將請求進一步交給:實現了IRouteHandler【實現了IHttpHandler接口】?的一個實例,下面是IRouteHandler的源碼:
namespace?System.Web.Routing
{?
????public?interface?IRouteHandler
????{??????
????????IHttpHandler?GetHttpHandler(RequestContext?requestContext);
????}
}
如果你想自己來實現這個接口然后在Web.Config中配置一下,那么請求就到了你自己的自定義的RouteHandler來執行后續的請求處理操作了。如果你使用的是默認的配置,那么請求會傳遞到MvcRouteHandler,那么請求f附加著HttpContext就會到達Asp.Net?MVC的處理中了。
第五階段:MvcRouteHandler創建Controller
請求到此,其實跟WebForm都是一致的,而后面才出現了一些不同,此時請求才真正的進入System.Web.Mvc控制的領域內。后面所有的東西我們都可以直接通過源碼來介紹了,而上面的所有的請求處理只能通過反射等方式來看或者學習,而后面的內容,我們可以幸福的直接看源碼了。那就跟我走進它的管道怎么流動的吧...
接著上面講,請求到了MvcRouteHandler類,而此類的源碼如下:
namespace?System.Web.Mvc
{
????using?System.Web.Routing;
????using?System.Web.SessionState;
????public?class?MvcRouteHandler?:?IRouteHandler
????{
????????private?IControllerFactory?_controllerFactory;
????????public?MvcRouteHandler()
????????{
????????}
????????public?MvcRouteHandler(IControllerFactory?controllerFactory)
????????{
????????????_controllerFactory?=?controllerFactory;
????????}
????????protected?virtual?IHttpHandler?GetHttpHandler(RequestContext?requestContext)
????????{
????????????requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
????????????return?new?MvcHandler(requestContext);
????????}
????????.....
}
MvcRouteHandler的GetHttpHandler方法被URLRouteModule調用,而看上面的紅色源碼部分我們看到,它將請求上下文交給了MVCHandler,并返回了MVCHandler。
而我查看源碼得知:MVCHandler實現了IHttpHandler,此時它的ProcessRequest方法被調用。且看MVCHandler的部分源代碼:
?
1?public?class?MvcHandler?:?IHttpAsyncHandler,?IHttpHandler,?IRequiresSessionState?
2?{
3?????????protected?internal?virtual?void?ProcessRequest(HttpContextBase?httpContext)?
4????????{
5?????????????SecurityUtil.ProcessInApplicationTrust(()?=>
6?????????????{
7?????????????????IController?controller;
8?????????????????IControllerFactory?factory;
9?????????????????ProcessRequestInit(httpContext,?out?controller,?out?factory);//初始化了ControllerFactory?
10????????????????try
11?????????????????{
12?????????????????????controller.Execute(RequestContext);
13?????????????????}
14?????????????????finally
15?????????????????{
16?????????????????????factory.ReleaseController(controller);
17?????????????????}
18?????????????});
19?????????}
20?}
從源碼中我們得知:請求交給MVCHandler后,它首先從ControllerBuilder獲取到當前的實現了IControllerFactory接口的ControllerFactory【也可以自己定義相關的CustomerControllerFactory,然后在Glable中注冊使用】。然后根據上下文中請求的Controller的字符串信息創建出實現了IController接口的控制器。然后調用了上面代碼中紅色部分,也就是controller.Execute(RequestContext);那此時請求就交給了controller。
第六階段:Controller調用Action返回ActionResult
?
由于此文過長,而且時間已經到了凌晨。源碼我就不貼了,簡單介紹一下流程,后面再做詳細贅述。
Controller的Execute方法是在基類ControllerBase中的方法,而此方法又調用ExecuteCore方法,然后此方法內部執行如下代碼:
string?actionName?=?RouteData.GetRequiredString("action");
if?(!ActionInvoker.InvokeAction(ControllerContext,?actionName))
{
????HandleUnknownAction(actionName);
}
首先從RouteData中獲取Action的名字,然后調用ActonInvoker的InvokeAction方法,調用Action執行。Action的返回的ActionResult的ExecuteResult(controllerContext)方法被執行,那此時就出現了分叉。如果直接返回的非ViewResult的話,那就直接協會到Respose流了返回客戶端了,如果是ViewResult的話,那就進入View的領域了。
第七階段:View視圖加載成Page類,并Render成Html
?
?
21?public?override?void?ExecuteResult(ControllerContext?context)
22?{
23?????????????if?(context?==?null)?
24????????????{
25?????????????????throw?new?ArgumentNullException("context");
26?????????????}
27?????????????if?(String.IsNullOrEmpty(ViewName))
28?????????????{
29?????????????????ViewName?=?context.RouteData.GetRequiredString("action");?
30????????????}
31?????????????ViewEngineResult?result?=?null;?
32????????????if?(View?==?null)?
33????????????{
34?????????????????result?=?FindView(context);
35????????????????View?=?result.View;?
36????????????}?
37????????????TextWriter?writer?=?context.HttpContext.Response.Output;
38?????????????ViewContext?viewContext?=?new?ViewContext(context,?View,?ViewData,?TempData,?writer);?
39????????????View.Render(viewContext,?writer);?
40????????????if?(result?!=?null)?
41????????????{?
42????????????????result.ViewEngine.ReleaseView(context,?View);?
43????????????}?
44??}
內部主要是通過ViewResult的FindView方法通過ViewEngine去加載具體的Aspx頁面或者是cshtml頁面生成對應的page類【針對Aspx】,然后再調用IView接口的Render方法將請求信息+ViewData的信息以等一塊渲染成Html并寫回到客戶端。
在此階段我們發現IViewEngine內部的實現這是到規定路徑下去加載Aspx頁面生成對應的ViewPage類。
IView接口的Render方法才是真正的去將Html和數據裝配的到一塊。自此請求結束。
總結:
?
客戶端請求→路由器→IIS服務器內核模塊HTTP.SYS→IIS→AspNet_isapi.dll→Asp.Net?Runtime→Application→IHttpModule....IHttpModule→MVCRouteModule→MVCRouteHandler→MVCHandler→ControllerFactory
→Controller→ActionInvoke→Aciton→ActiongResult.ExcuteReuslt()【如果是ViewResult】→IViewEngine?FindView→IView?Render→Response
最后附兩張關于此請求管道的兩張圖,以饗讀者。
?
?
引子
本文將主要演示怎么將多個Asp.Net?MVC項目部署到一個IIS站點中的例子,主要使用的是Asp.Net?MVC提供的區域的功能。
Asp.Net?MVC提供了區域的功能,可以很方便的為大型的網站劃分區域。可以讓我們的項目不至于太復雜而導致管理混亂,有了區域后,每個模塊的頁面都放入相應的區域內進行管理很方便。而隨著項目的復雜,每個開發人員開發的模塊呢也可能是一個完整的解決方案,而他要開發的UI項目呢只是主站點項目的一個區域,而如果把所有的UI項目放到一個UI項目,在團隊開發時就不很方便了,而我們想達到的效果是:每個模塊都對應一個UI項目【這里指Asp.Net?MVC項目】,最后部署的時候將子項目都配置成區域,而總的項目就是一個站點。
一、項目創建
首先創建一個主Asp.Net?MVC項目,然后創建一個子Asp。Net?MVC項目。項目的結構如下:
注:
1、AreasDemo【子項目,作為主項目的一個Area】、MvcAppMain【主Web項目】都是普通的Asp.Net?MVC3項目
2、MVCControllers是一個類庫項目
3、補充:Asp.Net?MVC的控制器:Controller是可以放到站點的任何DLL中的,它在搜索控制器時,會搜索站點下的所有DLL,當類符合條件:不是靜態類,類名以Controller結尾,實現了Controller基類【其實最主要是IController接口】的條件時它就會被識別為控制器。所以我們可以把控制器放到任何的其他項目中,只有將此控制器所在的DLL拷貝到、主站點的Bin目錄或者對應的DLL目錄就可以了。當然也可以放在默認的Web項目中的Controller文件夾下。
二、添加測試的Controller和Action
在子區域Web項目AreasDemo項目中添加一個Action,然后添加一個對應的視圖
在主Web項目MvcAppMain中添加一個HomeController和相應的Index.cshtml視圖文件。
在MVCAppMain項目中添加一個Admin區域,做測試使用。
項目最終截圖為:
我們看到,在主站點里添加了一個Admin區域后,默認創建了一個Areas文件夾,而且內部就是存放區域項目的頁面。
三、在子項目中添加Areas?Registration類
打開AreasDemo項目,添加一個AreasDemoRegistration類文件,其代碼如下:
?
1?public?class?AreasDemoRegistration?:?AreaRegistration//在主站點注冊區域
2?{
3?????public?override?string?AreaName
4?????{
5?????????get?{?return?"AreasDemo";?}
6?????}
7?????public?override?void?RegisterArea(AreaRegistrationContext?context)
8?????{
9?????????context.MapRoute(
10?????????????"AreasDemo_default",//路由名字,必須唯一
11???????"AreasDemo/{controller}/{action}/{id}",//路由規則
12???????new?{?action?=?"Index",?id?=?UrlParameter.Optional?}//默認值
13??????);
14?????}
15?}
其實就是一個普通的類,它實現了AreaRegistration基類。然后我們注冊區域路由就會在Global.asax的Application_Start事件方法中去執行注冊到主站點的路由表中。具體
可以參考Global.asax中紅色代碼部分:
?
16?protected?void?Application_Start()
17?{
18?????AreaRegistration.RegisterAllAreas();//注冊所有區域
19???RegisterGlobalFilters(GlobalFilters.Filters);
20?????RegisterRoutes(RouteTable.Routes);
21?}
至此我們基本的測試的基礎工作都做好了,下面就是到了部署階段了。
四、部署我們的項目
首先,我們需要將子項目的引用到主項目中。然后我們發布主項目到一個磁盤文件夾。然后,將子項目AreasDemo的Views文件夾拷貝到主項目發布后的文件夾對應的Areas\AreasDemo文件夾下。其中AreasDemo是areaname,此文件夾需要我們自己手動創建。然后,觀察發布后的bin目錄下有沒有AreasDemo.dll動態鏈接庫【Web子項目】。
然后,我們將此文件夾發布為IIS里的一個網站。最終演示效果為:
注:這是默認主Web的inde頁面
注:這是主站點里添加的Admin區域
注:這是子項目action請求返回的頁面
轉載于:https://www.cnblogs.com/hongwei8455/p/3572435.html
總結
- 上一篇: sql 汉字转首字母拼音
- 下一篇: Mac安装PhoneGap3