經典ASP NET MVC3 0入門詳解
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
注:由於本文原在word文件裡編寫,寫本文章時運用了大量截圖,直接複製到部落格裡,沒有顯示圖片,
圖片只是一些簡單的執行結果截圖,不影響大家學習
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請求返回的頁面
原文出處:點選開啟連結