本文章是由機器翻譯。
table.one { table-layout:fixed;width: 600px; font-size: 14px; color: #333; margin-bottom: 5px; } table.two { table-layout:fixed;width: 600px; border-top: 3px; border-top-color: #0569b5; border-top-style:solid; margin-bottom: 10px; } .tdwidth{ width:400px; } .tdwidth1{ width: 220px; } .SidebarInsights { border: 2px solid #008080; padding: 10px 20px 20px 20px; background-color: #e1e8f5; margin: 10px 0px 10px 0px; font-family: Verdana, Arial; font-size: 12px; padding-bottom: 7px; line-height: 16px; color: #333333; }
ASP.NET 有絕招
[生命和的 ASP.NET MVC 控制站的時間
Scott Allen
本文根據 ASP.NET MVC 架構鮮版。詳細資料如有變更。
內容
Factory 會造成所有的路由
Factory 的擴充性
執行是 Just 開頭
選取的屬性 (Attribute)
篩選屬性 (Attribute)
自訂動作的篩選器
取得結果
在動作是透過
控制站都是在 lynchpins 模型檢視控制器 (MVC) 設計模式。它們是在前一行先接收用戶端的要求] 和 [到應用程式的網域邏輯和資料所在的模型的指示,然後轉譯要求。控制站負責也選取檢視的使用者顯示資訊。
這的篇文章,我們將可以仔細分析 ASP.NET MVC 架構並可查看控制站的運作方式。我會說明在 Framework 與您的控制站的互動方式和如何您可能會影響這些互動。我將探討控制站的工廠、 控制器的動作及動作的篩選器動作以及結果。
我將會相當深入挖掘中,因此如果您正在尋找一般的 ASP.NET MVC 架構,簡介請參閱 Chris Tavares 的文件 」建置 Web 應用程式,而 Web Form."
Factory 會造成所有的路由
很難談不談論的路由的控制器的存留。路由表在 ASP.NET 應用程式中的會包含 ASP.NET 從傳入 URL 中擷取資訊,並將直接要求適當的軟體元件的路由模組所需的資訊。在 1 月的專欄,我探討使用 ASP.NET 路由模組,與 Web 表單 (「以 ASP.NET Web Form 路由".) 該的資料行中的請我建立我自己的路由處理常式,以執行 Web Form,但 ASP.NET MVC 架構提供路由處理常式,最後將會直接其中我們控制站的要求]。
處理要求這個 MVC 路由的處理常式,您要在應用程式啟動時,設定路由表。MVC 專案範本所提供,預設路由的設定存在於 Global.asax 檔案中,及 [圖 1 ] 所示。
[圖 1: 預設路由組態
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
其中一個路由的組態項目, [圖 1 ] 中會是名為的 URL] 範本的預設路由 」 {控制站} / 動作} / {識別碼} 」。 這個 URL 的範本會是路由引擎將用來第一個,請參閱如果這個路由符合的項目的傳入的 URL 模式。 URL,將會符合這類路由是 http://localhost/home/Index/。 當路由引擎會找到相符項目時, 它會再次使用 URL 範本模式提起從傳入的 URL 參數。 在這個範例中,字串 「 首頁 」 會變成控制器參數,因為它是在 {控制站} 的位置中,而且字串 「 索引成為 Action 參數。
匿名型別的物件建構為 MapRoute 將第三個參數會表示路由引擎在 URL 中找不到指定的參數,如果使用預設值。 在 http://localhost/home/Index/ 的情況下路由引擎不尋找在的 URL 」 識別碼資訊,但是它仍會傳遞以及預設值為空字串的識別碼參數]。 路由引擎會透過一個 RouteData 物件,將所有這些參數傳遞至路由的處理常式。
請注意路由引擎會知道任何有關 ASP.NET MVC 重要的。 引擎的唯一工作是分析 URL,並傳遞至資料的路由傳送處理常式的控制項。 MapRoute 方法內之 [圖 1 RegisterRoutes 方法叫用的會是延伸方法,由 MVC) 架構提供。 註冊 MapRoute 每個路由設定為使用 MvcRouteHandler 物件 — 這是 MVC Framework 所提供的路由傳送處理常式。 如您所見 1 月資料行中,它是要尋找要求的 HTTP 處理常式的路由傳送處理常式的工作 — 也就是實作 IHttpHandler 介面的物件。 MVC 應用程式,請這個物件將會型別 MvcHandler 的物件,而且在 MvcHandler 是其中處理成為有趣]。
[圖 2 ] 顯示一般 MVC 要求的處理流程。 控制項會到達,MvcHandler,the MvcHandler 時能夠從稍早在處理路由模組所產生,RouteData 中抽選控制器參數。 處理常式最後會將這個控制器] 參數,是字串值,傳送到控制器 Factory 中。 它是再建構,並傳回控制器 Factory 的責任。 MVC 應用程式中的所有控制站,實作 IController 介面。
[圖 2] 的一般 MVC 要求的控制流程
.MVC Framework 會提供一個預設控制器 Factory (適當命名為 DefaultControllerFactory),將會尋找所有類型的實作 IController,並將其名稱結束控制站的 appdomain 中所有組件中都搜尋。 因此,如果您知道要尋找 「 首頁 」 的控制器工廠,Factory 可以傳回 HomeController 無論命名空間或它存在於的組件的類別的新執行個體化的執行個體,只要它實作 IController。 這個問題屬於 MVC 的 「 使用慣例,透過組態 」 樣式。 沒有更 Factory 的故事,但是讓我們先完成 MvcHandler 處理。
一旦在 MvcHandler 的 IController 參考從工廠,它叫用控制器和等候工作的魔法,控制站上執行。 當執行為完整,[MvcHandler 會檢查如果控制器實作的 IDisposable 介面的而且如果時,叫會用 Dispose 來清除 Unmanaged 資源控制站上。
Factory 的擴充性
控制器 Factory 會是一個金鑰的擴充性點,MVC ASP.NET Framework 中。 雖然 Framework 所提供的預設 Factory 可以在您的解決方案中找到任何地方的 HomeController,它只能具現化,HomeController 如果您提供無參數的建構函式。 這項限制會是小組遵循相依性的反向原則,並插入透過其建構函式的控制站的相依性的問題。 例如,考慮 EmployeeController (所示 [圖 3 提供) 需要有人將一個的記錄元件傳遞至其唯一的建構函式。
[圖 3 EmployeeController
public class EmployeeController : IController
{
public EmployeeController(ILogger logger)
{
_logger = logger;
}
public void Execute(RequestContext requestContext)
{
// ...
}
ILogger _logger;
}
幸運的是,您可以建立自訂的 Factory。 實作 IControllerFactory 介面的任何類別都是一個的候選,而且您僅需要實作 CreateController 和 ReleaseController 方法。 不過,反向的控制項的容器,例如 StructureMap、 Unity、 Ninject 及城堡專案的 Windsor 隨時可,並會在這個案例中為完整適合。 事實上, CodePlex 上的 MVC contrib 專案 包含 IControllerFactory 上面所列之容器的所有的實作。
如果要做為您的控制項容器的反向的 StructureMap 您可以參考 StructureMap 和 MvcContrib.StructureMap) 組件,然後撰寫程式碼,如 [圖 4 ] 所示。 此清單中的 InitializeContainer 方法先告訴 StructureMap 的 ILogger 必要時,請使用 SqlServerLogger 型別。 程式碼,然後設定控制器 Factory,整個應用程式使用的目前的 ControllerBuilder SetControllerFactory 方法。 過程要求在 MvcHandler 會要求這個相同的 ControllerBuilder 目前設定的 Factory,並使用 StructureMapControllerFactory,以取代預設 Framework Factory。
[圖 4 初始化容器
protected void Application_Start()
{
InitializeContainer();
RegisterRoutes(RouteTable.Routes);
}
private void InitializeContainer()
{
StructureMapConfiguration
.ForRequestedType<ILogger>()
.TheDefaultIsConcreteType<SqlServerLogger>();
ControllerBuilder.Current.SetControllerFactory(
new StructureMapControllerFactory());
}
從 MVC contrib 專案在 StructureMapControllerFactory 不會繼承 MVC 架構的預設控制器 Factory,並仍使用我稍早描述尋找控制器型別具現化時,慣例。不過,Factory 會用來 StructureMap 具現化的控制站並 StructureMap 會知道如何使用參數型建構函式。[圖 4 ] 所示的組態是一切處理 http://localhost/Employee/ 的要求。StructureMap 會產生 [圖 3 在 EmployeeController,藉由傳入 SqlServerLogger 參考。
[圖 5 類別階層架構
執行是 Just 開頭
稍早我所述,MvcHandler 叫用控制站的 Execute 方法,等候,然後清除。這是因為在 MvcHandler 只知道透過 IController 介面的控制站。如果要撰寫您的應用程式在這個層級則您無法直接衍生所有從 IController 介面的控制站,並提供在 Execute 方法實作。不過,ASP.NET MVC 架構所提供的更豐富的執行模型,控制站,透過的 [圖 5 ] 所示的類別階層架構。
預設的情況下,您將加入至 ASP.NET MVC 專案的控制站會衍生自 System.Web.mvc.controller 類別。加入新的控制站是在 [方案總管中的的 [控制器] 資料夾上按一下滑鼠右鍵,並選取 [新增] 其中一個方法 | 控制站可讓您在 [圖 6 顯示對話方塊。請記住的控制器 Factory,找到您的控制站名稱之後必須搭配控制器。
控制站的基底類別,引入了動作的概念。動作會是控制站上做為 MVC 應用程式中最後的要求目的地的方法。稍早,我指出 ASP.NET 的路由模組將會移除 「 首頁 」 為控制器參數,從 URL 的 http://localhost/home/Index/ 出。這是足夠的資訊,適當的控制器來路由傳送要求。路由的模組,也會挑選出 「 索引 」 為動作參數。當 [MvcHandler 會告訴 HomeController,執行時,是由基底的控制器) 類別,檢查此動作的參數,和在叫用適當的控制器的方法內撰寫邏輯。大部分的這個動作的路由邏輯,位於公用 ControllerActionInvoker 類別內。
[圖 6 加入控制器
[圖 7 ] 是由 ASP.NET MVC 專案範本,HomeController。這些公用執行個體方法,索引,並在代表用戶端要求的家庭 / 索引時,Framework 會叫用的動作,關於,/ 和首頁 / 關於 /,分別。任何公用執行個體方法可以作為一個控制器的動作,只要在 Framework 可以判斷特定的動作是正確叫用 (Invoke) 動作 (也就是說小心方法多載)。其他規則在播放時,有.Framework 正在搜尋要叫用 (Invoke) 的動作。您可以影響架構的所選擇的動作,建立其他規則動作選取,並管理您的動作使用屬性 (Attribute) 的行為。
[圖 7 HomeController
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
}
選取的屬性 (Attribute)
您可以裝飾提供 Framework 其他資訊,它會選取要叫用 (Invoke) 的動作時要考慮的選取器屬性在控制器動作。 例如,至控制器的方法中加入 [NonAction] 屬性將會導致方法從可用的動作清單中排除。 您也可以在特定的動作名稱提供方法。 預設狀況下,方法的名稱也是動作名稱,但如果您將 [ActionName("Help")] 在您 HomeController 相關方法,則 about 」 不再為控制器正確的動作。 而,有關方法的動作名稱會是 [說明],而且,Framework 會叫用,有關方法的要求,例如 / 首頁 / 說明 /。
其中的特別重要的選取器屬性是在 AcceptVerbs 屬性。 這個屬性只允許將 Framework 動詞命令在屬性中列出的其中一個符合目前 HTTP 要求的動詞命令時,請選取的動作。 例如,裝飾為方法 with[AcceptVerbs(HttpVerbs.post)] 表示的方法可以叫只用做為 HTTP POST 作業的動作。 很重要以選取您的控制器動作,適當的動詞命令特別是如果動作會修改在伺服器上的狀態。 可能進一步,請參魷 \ cs6 \ f1 \ cf6 \ lang1024 Stephen Walther ASP.NET MVC 提示 #46 .
篩選屬性 (Attribute)
動作的篩選器將是屬性的您,另一個的型別,您可以將執行的動作。 動作的篩選器您可以加入快取、 驗證和錯誤處理行為,您使用宣告式程式設計模型的動作。 篩選器屬性的範例都是在 [圖 7 ] 中的 [HomeController HandleError] 屬性。 您可以將這個屬性 (Attribute) 套用至個別的動作,或將屬性加入要套用 [行為] 的控制器的所有動作至 Controller 類別至。
當一個 HandleError 屬性出現在 [動作] 上,而且動作會擲回例外狀況時 MVC 架構會尋找具有 「 錯誤 」 的名稱在檢視-控制器的檢視資料夾,中的第一個,然後也在共用的檢視資料夾中。 [錯誤] 檢視可讓您顯示易懂的錯誤網頁給使用者。 您也可以將例外狀況對應到特定的檢視,使用更明確的 HandleError 屬性。 例如,[HandleError(ExceptionType=typeof(SqlException),檢視 ="DatabaseError)] 會對應至名為 DatabaseError 」 檢視的未處理的 SqlException。 動作的篩選器 MVC Framework 所提供的其餘部分是由 [圖 8所述。
[圖 8 個動作的篩選器 |
名稱 | 描述 |
OutputCacheAttribute | 類似於在 ASP.NET Web Form 中 OutputCache 指示詞。 OutputCache 屬性 (Attribute) 允許快取控制站的輸出.MVC Framework。 |
ValidateInputAttribute | 類似於 Web Form 中 ValidateRequest 屬性。 預設的情況下,MVC) 架構會檢查傳入的 HTTP 要求的 HTML 或其他危險的輸入。 如果偵測到,將會引發例外狀況。 您可以使用這個屬性,停用要求驗證。 |
AuthorizeAttribute | authorize 屬性可讓您將控制器的動作上的宣告式的授權檢查。 屬性可以限制特定角色的使用者的動作。 當您建立只可供系統管理員的角色中的使用者的動作時,您可以使用這個屬性。 |
ValidateAntiForgeryTokenAttribute | 這個屬性可以是其中一個解決方案,協助防止跨站台的一半要求 forgeries (CSRF)。 它可讓驗證的使用者特定語彙基元的 HTTP POST,Framework。 如需有關 CSRFs 的詳細資訊,請參閱 < 防止跨站台要求偽造 (CSFR) 使用 ASP.NET MVC AntiForgeryToken() Helper ." |
自訂動作的篩選器
您可以建立您自己動作篩選器括住的動作,使用自訂邏輯。 [圖 9 ] 中的程式碼都是簡單的記錄動作篩選器,可以寫入輸出視窗的 Visual Studio 在偵錯期間代碼。 我們也可以套用這個屬性的個別的動作,或我們可以將這個屬性 (Attribute) 放在控制器類別在控制器上的所有動作的記錄]。
[圖 9 A 記錄動作的篩選器
public class LogAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("Action Executing", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("Action Executed", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("Result Executing", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("Result Executed", filterContext.RouteData);
}
void Log(string stageName, RouteData routeData)
{
Debug.WriteLine(
String.Format("{0}::{1} - {2}",
routeData.Values["controller"],
routeData.Values["action"],
stageName));
}
}
您可以看到有四個基底 ActionFilter 類別所提供的虛擬方法。您可以覆寫一或多個不只是建置前和後續處理的控制器動作,但也套用這些方法,並在後續處理控制器動作的結果。之前的動作執行、 OnActionExecuting 方法會引發,並動作完成時,OnActionExecuted 方法將引發 (及即使動作,擲回未處理例外狀況,會引發)。同樣地,結果執行和 OnResultExecuted 方法會引發之後之前,會引發 OnResultExecuting 方法。
內容參數傳遞至動作的篩選器方法可讓您檢查 HTTP 要求、 HTTP 內容、 在路由傳送的資料和更多。擲回的例外狀況,從其中一個這些方法將會中止要求處理流程。例外狀況是一個很有用的工具,如果您正在撰寫的 ActionFilter 檢查先決條件,在環境中。
取得結果
MVC 控制器動作的成功執行會產生衍生自 ActionResult 的物件。呈現檢視,並將重新導向至新的 URL 的瀏覽器是 [兩個的您可能想要從您的控制器結果的有效類型]。[圖 10 顯示完整 ActionResult 衍生型別的清單。
[圖 10 ActionResult 衍生型別 |
名稱 | Framework 行為 | 產生方法 |
ContentResult | 您可以將字串值直接將 HTTP 回應。 | 內容 |
EmptyResult | 不會寫入 HTTP 回應。 | |
FileContentResult | 會在檔案 (表示為位元組陣列) 的內容,並將內容寫入為 HTTP 回應。 | 檔案 |
FilePathResult | 在指定的位置會在檔案的內容,並寫入 HTTP 回應的內容]。 | 檔案 |
FileStreamResult | 接受控制器所產生檔案資料流,並寫入 HTTP 回應的資料流中。 | 檔案 |
HttpUnauthorizedResult | 授權檢查失敗時,授權篩選器會使用一個特殊的結果。 | |
JavaScriptResult | 回應用戶端使用用戶端執行指令碼。 | JavaScript |
JsonResult | 回應用戶端 JavaScript Object Notation (JSON) 資料。 | json |
RedirectResult | 重新導向至新的 URL 的用戶端。 | 重新導向 |
RedirectToRouteResult | 呈現指定的檢視 (通常是在 AJAX 的案例中使用) 的 HTML 片段的回應。 | RedirectToRoute / RedirectToAction |
PartialViewResult | 呈現指定的檢視 (通常是在 AJAX 的案例中使用) 的 HTML 片段的回應。 | PartialView |
ViewResult | 呈現指定的檢視,並以 HTML 用戶端回應。 | 檢視 |
請注意在控制器的動作不會需要直接具現化其中一種型別。而,控制器的動作可以叫用方法的名稱以 [圖 10] 顯示,產生的結果。這些方法被繼承自 MVC 控制器的基底類別中。也是值得注意不需要在控制器的動作傳回 ActionResult 物件。如果控制器可傳回以外的 ActionResult,Framework 會將物件轉換成字串,並包裝在 ContentResult (這只是將字串寫入 HTTP 回應到) 字串。一個控制站傳回 void,就會產生一個 EmptyResult。
ActionResult 類別定義 ExecuteResult 方法每個 [圖 10 ] 中的型別將會覆寫。這個的方法,ControllerActionInvoker 由叫用 (相同的物件,呼叫控制器的動作。叫用 (Invoke),每個搜尋結果會格外謹慎的成功地傳送到 [用戶端的結果所需的所有小細節。例如,JavaScript 結果在 [HttpUnauthorizedResult 將設定,在 401 (未授權) 回應的 HTTP 狀態碼時,會設定"application / x-javascript",回應的內容類型標頭。
在一般的回應,從一個控制器的動作將會是一個 ViewResult。您已經知道這個結果,先前的程式碼清單中,所有您的動作已叫用控制站的檢視方法,並傳回結果。這會是 「 使用慣例,透過組態,」 的另一個範例,因為在 ViewResult 會使用此預設值的方式時尋找一個檢視,請在控制站的檢視資料夾中,並以符合該動作的檔案名稱。舉例來說,views\home\about.aspx 是傳統檢視,需主控制器的動作。多載的版本,檢視方法的可讓您以明確地在檢視的名稱。
在動作是透過
這個月,我已經採取深入探討的抽象概念,並行為周圍的 ASP.NET MVC 控制站。yo u 現在應該已在良好的 MVC) 架構的發現如何掌握、 建立,並使用控制器,以及如何連結到圍繞控制站 MVC) 架構擴充性點。在這個資料行的下一版,我就看方針] 和 [將這些控制站在實際的應用程式中運作的最佳作法。
了解: Helper 方法
如果您覺得奇怪第一次出現在.Framework 時,使用 Helper 方法動作的結果 (view()、 content()),您可能會想要知道如何所有相關且背後的特定的設計決策是。
Helper 方法,傳回動作的結果的後面,本文也,是 99%的 MVC 開發人員的時間撰寫 MVC 應用程式即將要花撰寫控制器的動作。我們想要確定一般的方法是在初始狀態、 可讀取,和盡為宣告。
例如上,您仍然可以撰寫一個動作的方法就像這樣:
public ActionResult Foo {
// ... do stuff ...
ViewData.Model = myModel;
return new ViewResult {ViewName = "Foo", ViewData = this.ViewData};
}
我們要清除這一點,讓我們做了一些的調整,您可以看到:
public ActionResult Foo {
// ... do stuff ...
return new View(myModel);
}
這是語言的多的宣告式的方法,(儘管使用非常重要)。 當您讀取該動作的方法時, 它會反映您的目的。 " 我要傳回檢視,其中包含這個模型 」。
--Phil Haak,資深程式管理員是 Microsoft
您問題或意見寄至 xtrmasp@Microsoft.com.
K Scott Allen 是 Pluralsight 的技術人員的成員 OdeToCode 的創始者。 您可以與 Scott 在 Scott@OdeToCode.com 或讀取在他的部落格 odetocode.com/blogs/Scott.