1. 程式人生 > 其它 >第4章 使用 Razor Pages 建立網站(ASP.NET Core in Action, 2nd Edition)

第4章 使用 Razor Pages 建立網站(ASP.NET Core in Action, 2nd Edition)

本章重點

  • RazorPages和模型檢視-控制器(MVC)設計模式簡介
  • 在ASP.NET Core中使用RazorPages
  • 在RazorPages和MVC控制器之間進行選擇
  • 使用Action結果控制應用程式流

在第3章中,您瞭解了中介軟體管道,它定義了ASP.NET Core應用程式如何響應請求。在將請求傳遞到管道中的下一個中介軟體之前,每個中介軟體都可以修改或處理傳入的請求。

在ASP.NET Core web應用程式中,中介軟體管道通常包括EndpointMiddleware。這通常是編寫大部分應用程式邏輯的地方,呼叫應用程式中的各種其他類。它也是使用者與應用程式互動的主要入口。它通常採用以下三種形式之一:

  • 為使用者直接使用而設計的HTML web應用程式——如果應用程式直接由使用者使用,就像傳統web應用程式一樣,那麼RazorPages負責生成使用者互動的網頁。它處理URL請求,接收使用表單釋出的資料,並生成使用者用於檢視和導航應用程式的HTML。
  • 為另一臺機器或程式碼使用而設計的API——web應用程式的另一種主要可能是用作後端伺服器程序、移動應用程式或客戶端框架的API,用於構建單頁應用程式(SPA)。在這種情況下,應用程式以機器可讀格式(如JSON或XML)提供資料,而不是以人為中心的HTML輸出。
  • 一個HTML web應用程式和一個API——也可以有同時滿足這兩種需求的應用程式。這可以讓您在應用程式中共享邏輯的同時滿足更廣泛的客戶。

在本章中,您將瞭解ASP.NET Core如何使用RazorPages來處理第一個選項,即建立伺服器端呈現的HTML頁面。您將首先檢視模型-檢視-控制器(MVC)設計模式,以瞭解通過使用它可以獲得的好處,並瞭解為什麼它被如此多的web框架採用為構建可維護應用程式的模型。

接下來,您將瞭解MVC設計模式如何應用於ASP.NET Core。MVC模式是一個廣泛的概念,可以應用於各種情況,但ASP.NET Core中的使用主要是作為UI抽象。您將看到RazorPages是如何實現MVC設計模式的,它是如何在ASP.NET Core MVC框架之上構建的,並比較這兩種方法。

接下來,您將看到如何將RazorPages新增到現有應用程式中,以及如何建立第一個RazorPage。您將學習如何定義應用程式接收請求時要執行的頁面處理程式,以及如何生成可用於建立要返回的HTTP響應的結果。

在本章中,我不會介紹如何建立WebAPI。Web API仍然使用ASP.NET Core MVC框架,但它們的使用方式與RazorPages略有不同。它們不返回直接顯示在使用者瀏覽器中的網頁,而是返回格式化後的資料以供程式碼使用。Web API通常用於向移動和Web應用程式或其他伺服器應用程式提供資料。但它們仍然遵循相同的通用MVC模式。您將在第9章中瞭解如何建立Web API。

注:本章是ASP.NET Core中RazorPages和MVC的第一章。正如我已經提到的,這些框架通常負責處理應用程式的所有業務邏輯和UI程式碼,因此,可能並不奇怪,它們很大而且有些複雜。接下來的五章都討論了構成MVC和RazorPages框架的MVC模式的不同方面。

在本章中,我將嘗試為您準備接下來的每一個主題,但您可能會發現,在這個階段,有些行為感覺有點像魔術。試著不要太在意所有部件是如何結合在一起的;專注於所處理的具體概念。當我們在本書的其他部分中介紹相關細節時,這一切都將變得清晰。

4.1 RazorPages簡介

RazorPages程式設計模型在ASP.NET Core 2.0中引入,作為構建伺服器端呈現的“基於頁面”網站的一種方式。它建立在ASP.NET Core基礎設施之上,提供了一種簡化的體驗,在可能的情況下使用約定來減少所需的樣板程式碼和配置的數量。

定義:基於頁面的網站是一個使用者在多個頁面之間瀏覽、將資料輸入表單並生成內容的網站。這與遊戲或單頁應用程式(SPA)等應用程式形成了鮮明對比,後者在客戶端具有很強的互動性。

在第2章中,您已經看到了RazorPage的一個非常基本的示例。在本節中,我們將從稍微複雜一點的RazorPage開始,以更好地理解RazorPage的總體設計。我將展示

  • 典型RazorPage的示例
  • MVC設計模式及其如何應用於RazorPages
  • 如何將RazorPages新增到應用程式

在本節結束時,您應該很好地瞭解RazorPages背後的總體設計,以及它們與MVC模式的關係。

4.1.1 探索典型的RazorPage

在第二章中,我們看到了一個非常簡單的RazorPage。它不包含任何邏輯,只是呈現了關聯的Razor檢視。例如,如果您正在構建一個內容眾多的營銷網站,這種模式可能很常見,但更常見的是,RazorPages將包含一些邏輯、從資料庫載入資料或使用表單允許使用者提交資訊。

為了讓您更瞭解典型RazorPages的工作原理,在本節中,我們將簡要介紹一個稍微複雜一些的RazorPage。此頁面取自待辦事項列表應用程式,用於顯示給定類別的所有待辦事項。現在我們沒有關注HTML的生成,所以下面的列表只顯示RazorPage的PageModel程式碼。

清單4.1 用於檢視給定類別中所有待辦事項的RazorPage

public class CategoryModel : PageModel
{
private readonly ToDoService _service; //ToDoService是使用依賴注入在模型建構函式中提供的 public CategoryModel(ToDoService service) { _service = service; } public ActionResult OnGet(string category) //OnGet處理程式接受一個引數category。 { Items = _service.GetItemsForCategory(category); //處理程式呼叫ToDoService以檢索資料並設定Items屬性return Page(); //返回一個PageResult,表明應呈現Razor檢視 } public List<ToDoListModel> Items { get; set; } //Razor檢視在渲染時可以訪問Items屬性。 }

該示例仍然相對簡單,但與第2章中的基本示例相比,它展示了多種功能:

  • 頁面處理程式OnGet接受方法引數category。RazorPage基礎結構使用來自傳入請求的值在一個稱為模型繫結的過程中自動填充此引數。我在第6章中詳細討論了模型繫結。
  • 處理程式不直接與資料庫互動。相反,它使用提供的類別值與ToDoService進行互動,ToDoService通過依賴注入作為建構函式引數注入。
  • 處理程式在方法末尾返回Page(),以指示應呈現關聯的Razor檢視。在這種情況下,return語句實際上是可選的;按照慣例,如果頁面處理程式是一個void方法,Razor檢視仍將被呈現,就像您在方法末尾呼叫了return page()一樣。
  • RazorView可以訪問CategoryModel例項,因此它可以訪問處理程式設定的Items屬性。它使用這些項來構建最終傳送給使用者的HTML。

清單4.1的RazorPage中的互動模式顯示了一種常見的模式。頁面處理程式是RazorPage的中央控制器。它接收使用者的輸入(類別方法引數),呼叫應用程式(ToDoService)的“大腦”,並將資料(通過Items屬性)傳遞給Razor檢視,Razor將生成HTML響應。這看起來像模型-檢視-控制器(MVC)設計模式。

根據您在軟體開發方面的背景,您可能以前遇到過某種形式的MVC模式。在web開發中,MVC是一種常見的模式,用於Django、Rails和Spring MVC等框架中。但由於MVC是一個廣泛的概念,您可以在從移動應用程式到富客戶端桌面應用程式的所有應用程式中找到MVC。希望這表明瞭如果正確使用該模式可以帶來的好處!在下一節中,我們將瞭解MVC模式的一般情況,以及ASP.NET Core如何使用它。

4.1.2 MVC設計模式

MVC設計模式是設計具有UI的應用程式的常見模式。最初的MVC模式有許多不同的解釋,每一種解釋都側重於模式的稍微不同的方面。例如,最初的MVC設計模式是使用在富客戶端圖形使用者介面(GUI)應用程式而不是web應用程式,因此它使用了與GUI環境相關的術語和範例。不過,從根本上講,該模式旨在將資料的管理和操作與其視覺化表示分開。

在深入研究設計模式本身之前,讓我們考慮一個典型的請求。想象一下,應用程式的使用者從顯示待辦事項列表類別的前一部分請求RazorPage。圖4.1顯示了RazorPage如何處理請求的不同方面,所有這些方面結合起來生成最終響應。

  

 圖4.1請求RazorPages應用程式的待辦事項列表頁面。不同的“元件”處理請求的每個方面。

通常,MVC設計模式由三個“元件”組成:

  • 模型-這是需要顯示的資料,即應用程式的全域性狀態。它可以通過清單4.1中的ToDoService訪問。
  • 檢視-顯示模型提供的資料的模板。
  • 控制器-這將更新模型並向檢視提供顯示資料。此角色由RazorPages中的頁面處理程式承擔。這是清單4.1中的OnGet方法。

MVC設計模式中的每一個元件都負責整個系統的一個方面,當它們結合起來時,可以用來生成UI。待辦事項列用例將MVC視為使用RazorPages的web應用程式,這時請求也可以理解為在桌面GUI應用程式中單擊按鈕。

通常,應用程式響應使用者互動或請求時的事件順序如下:

  1. 控制器(RazorPage處理程式)接收請求。
  2. 根據請求,控制器或者使用注入的服務從應用程式模型獲取請求的資料,或者更新構成模型的資料。
  3. 控制器選擇要顯示的檢視,並將模型的表示傳遞給它。
  4. 檢視使用模型中包含的資料來生成UI。

當我們以這種格式描述MVC時,控制器(RazorPage處理程式)充當互動的入口點。使用者與控制器通訊以發起互動。在web應用程式中,這種互動採用HTTP請求的形式,因此當接收到對URL的請求時,控制器會處理它。

根據請求的性質,控制器可以採取各種行動,但關鍵點是,這些行動是使用應用程式模型進行的。這裡的模型包含應用程式的所有業務邏輯,因此它能夠提供請求的資料或執行操作。

注:在MVC的描述中,模型被認為是一個複雜的野獸,包含了如何執行動作的所有邏輯,以及任何內部狀態。RazorPage PageModel 類不是我們所討論的模型!不幸的是,與所有軟體開發一樣,命名同樣是個難題。

考慮檢視電子商務應用程式的產品頁面的請求。控制器將接收請求,並知道如何聯絡屬於應用程式模型的某個產品服務。這可能會從資料庫中獲取所請求產品的詳細資訊,並將其返回給控制器。

或者,設想一個控制器接收到向用戶的購物車新增產品的請求。控制器將接收請求,並且很可能會呼叫模型上的方法來請求新增產品。然後,模型將更新使用者購物車的內部表示,例如,向儲存使用者資料的資料庫表中新增新行。

提示:您可以將每個RazorPage處理程式視為專注於單個頁面的小型控制器。每個web請求都是對生成響應的控制器的一個獨立呼叫。儘管有許多不同的控制器,但處理程式都與同一個應用程式模型互動。

 模型更新後,控制器需要決定生成什麼響應。使用MVC設計模式的優點之一是,表示應用程式資料的模型與稱為檢視的資料的最終表示分離。控制器負責決定響應是否應生成HTML檢視,是否應將使用者傳送到新頁面,或者是否應返回錯誤頁面。

模型獨立於檢視的優點之一是它提高了可測試性。UI程式碼通常很難測試,因為它依賴於環境,任何編寫過模擬使用者單擊按鈕並輸入表單的UI測試的人都知道它通常很脆弱。通過保持模型獨立於檢視,可以確保模型易於測試,而不依賴於UI構造。由於模型通常包含應用程式的業務邏輯,這顯然是一件好事!

檢視可以使用控制器傳遞給它的資料來生成適當的HTML響應。檢視僅負責生成資料的最終表示;它不涉及任何業務邏輯。

這就是與web應用程式相關的MVC設計模式的全部內容。許多與MVC相關的容易混淆的概念似乎源於對略有不同的框架和應用程式型別的術語使用略有不同。在下一節中,我將展示ASP.NET Core框架如何在RazorPages中使用MVC模式,以及該模式的更多例項。

4.1.3 將MVC設計模式應用於RazorPage

在上一節中,我討論了MVC模式,因為它通常用於web應用程式;RazorPages使用此模式。但是ASP.NET Core還包括一個名為ASP.NET Core MVC的框架。這個框架(毫不奇怪)非常接近MVC設計模式,使用控制器和Action方法代替RazorPages和頁面處理程式。RazorPages直接構建在底層ASP.NET Core MVC框架之上,使用底層MVC框架來實現其行為。

如果您願意,可以完全避免RazorPages,直接在ASP.NET Core中使用MVC框架。這是早期版本ASP.NET Core和早期版本ASP.NET中的唯一選項。

提示:我在第4.2節中更深入地研究了RazorPages和MVC框架之間的選擇。

在本節中,我們將更深入地瞭解MVC設計模式如何應用於ASP.NET Core中的RazorPages。這也有助於澄清RazorPages的各種功能的作用。

RazorPages使用MVC還是MVVM?
我偶爾看到人們將RazorPages描述為使用模型-檢視-檢視-模型(MVVM)設計模式,而不是MVC設計模式。就我個人而言,我不同意,但值得注意的是其中的差異。
MVVM是一種UI模式,常用於移動應用程式、桌面應用程式和一些客戶端框架。它與MVC的不同之處在於檢視和檢視模型之間存在雙向互動。檢視模型告訴檢視要顯示什麼,但檢視也可以直接在檢視模型上觸發更改。它通常用於雙向資料繫結,其中檢視模型“繫結”到檢視。

正如您在前幾章中所看到的,ASP.NET Core使用RoutingMiddleware和EndpointMiddleware的組合實現RazorPage端點,如圖4.2所示。一旦早期中介軟體處理了請求(假設沒有一箇中間件處理過請求並使管道短路),路由中介軟體將選擇應該執行哪個RazorPagehandler,並且端點中介軟體執行頁面處理程式。

 圖4.2 典型ASP.NET Core應用程式的中介軟體管道。請求由每個中介軟體依次處理。如果請求到達路由中介軟體,中介軟體將選擇一個端點(如RazorPage)來執行。端點中介軟體執行所選端點。

中介軟體經常處理一些簡單問題或狹義的請求,例如檔案請求。對於超出這些功能或具有許多外部依賴性的需求,需要一個更健壯的框架。RazorPages(和/或ASP.NET Core MVC)可以提供此框架,允許與應用程式的核心業務邏輯互動並生成UI。它處理從將請求對映到適當的控制器到生成HTML或API響應的所有事情。

在MVC設計模式的傳統描述中,只有一種型別的模型,它包含所有非UI資料和行為。控制器根據需要更新該模型,然後將其傳遞給檢視,檢視使用該模型生成UI。

討論MVC時的一個問題是它使用的模糊和模稜兩可的術語,例如“控制器”和“模型”。特別是模型,它是一個過載的術語,通常很難確定它到底指的是什麼-它是物件、物件集合還是抽象概念?即使是ASP.NET Core也使用“模型”一詞來描述幾個相關但不同的元件,您很快就會看到。

將請求定向到RazorPage並構建繫結模型

應用程式收到請求的第一步是將請求路由到適當的RazorPage處理程式。讓我們再次考慮類別待辦事項列表頁面,從列表4.1開始。在此頁面上,您將顯示具有給定類別標籤的專案列表。如果您正在檢視類別為“Simple”的專案列表,則會向/categy/Simple路徑發出請求。

路由採用請求的標頭和路徑/categy/Simple,並將其對映到預先註冊的模式列表。這些模式每個都匹配到單個RazorPage和頁面處理程式的路徑。您將在下一章中瞭解有關路由的更多資訊。

提示:我使用術語RazorPage來指Razor檢視和包含頁面處理程式的PageModel的組合。注意,Page-Model類不是我們在描述MVC模式時所指的“模型”。它完成了其他角色,您將在本節稍後部分看到。

一旦選擇了頁面處理程式,就會生成繫結模型(如果適用)。該模型是基於傳入請求、標記為繫結的PageModel的屬性以及頁面處理程式所需的方法引數構建的,如圖4.3所示。繫結模型通常是一個或多個標準C#物件,其屬性對映到請求的資料。我們將在第6章中詳細討論繫結模型。

 圖4.3將請求路由到控制器並構建繫結模型。對/categy/SimpleURL的請求導致執行CategoryModel.OnGet頁面處理程式,並傳遞填充的繫結模型category。

定義:繫結模型是一個或多個物件,充當頁面處理程式所需的請求資料中提供的資料的“容器”。

在本例中,繫結模型是一個簡單的字串category,它繫結到“simple”值。此值在請求URL的路徑中提供。還可以使用更復雜的繫結模型,其中填充了多個屬性。

本例中的繫結模型對應於OnGet頁面處理程式的方法引數。RazorPage的例項是使用其建構函式建立的,繫結模型在執行時傳遞給頁面處理程式,因此可以使用它來決定如何響應。對於本例,頁面處理程式使用它來決定要在頁面上顯示哪些待辦事項。

使用應用程式模型執行處理程式

頁面處理程式作為MVC模式中的控制器的作用是協調生成對其處理的請求的響應。這意味著它只能執行有限數量的操作。特別是,它應該

  • 驗證所提供的繫結模型中包含的資料對請求有效
  • 使用服務在應用程式模型上呼叫適當的操作
  • 根據應用程式模型的響應選擇要生成的適當響應

圖4.4顯示了在應用程式模型上呼叫適當方法的頁面處理程式。在這裡,您可以看到應用程式模型是一個有點抽象的概念,它封裝了應用程式的其餘非UI部分。它包含域模型、許多服務和資料庫互動。

 圖4.4 執行時,一個動作將呼叫應用程式模型中的適當方法。

定義:域模型將複雜的業務邏輯封裝在一系列類中,這些類不依賴於任何基礎設施,並且可以很容易地進行測試。

頁面處理程式通常呼叫應用程式模型中的單個點。在我們檢視待辦事項列表類別的示例中,應用程式模型可能會使用各種服務來檢查當前使用者是否被允許檢視某些專案、搜尋給定類別中的專案、從資料庫載入詳細資訊或從檔案載入與專案相關聯的圖片。

假設請求有效,應用程式模型將向頁面處理程式返回所需的詳細資訊。然後由頁面處理程式選擇要生成的響應。

使用檢視模型構建HTML

一旦頁面處理程式呼叫了包含應用程式業務邏輯的應用程式模型,就應該生成響應了。檢視模型捕獲檢視生成響應所需的詳細資訊。

定義:MVC模式中的檢視模型是檢視呈現UI所需的所有資料。這通常是應用程式模型中包含的資料的一些轉換,以及呈現頁面所需的額外資訊,例如頁面標題。

術語檢視模型在ASP.NET Core MVC中廣泛使用,它通常指傳遞給Razor檢視進行渲染的單個物件。然而,使用RazorPages,Razor檢視可以直接訪問RazorPage的頁面模型類。因此,RazorPage PageModel通常充當RazorPage中的檢視模型,Razor檢視所需的資料通過屬性公開,如您之前在清單4.1中所見。

注意:RazorPages使用PageModel類本身作為Razor檢視的檢視模型,將所需資料作為屬性公開。

Razor檢視使用頁面模型中公開的資料來生成最終的HTML響應。最後,通過中介軟體管道將其傳送回用戶的瀏覽器,如圖4.5所示。

 圖4.5 頁面處理程式通過在PageModel上設定屬性來構建檢視模型。生成響應的是檢視。

需要注意的是,儘管頁面處理程式選擇是否執行檢視和要使用的資料,但它並不控制生成的HTML。是檢視本身決定了響應的內容。

把這一切放在一起:一個完整的RazorPage請求

現在您已經看到了使用RazorPages在ASP.NET Core中處理請求的每一個步驟,讓我們將其從請求到響應放在一起。圖4.6顯示瞭如何組合這些步驟來處理顯示“簡單”類別待辦事項列表的請求。傳統的MVC模式在RazorPages中仍然可見,它由頁面處理程式(控制器)、檢視和應用程式模型組成。

  

圖4.6 “簡單”類別中待辦事項列表的完整RazorPages請求

到目前為止,您可能會認為整個過程似乎相當複雜,需要這麼多步驟才能顯示一些HTML!為什麼不允許應用程式模型直接建立檢視,而不是必須使用頁面處理程式方法來回跳舞?

整個過程的關鍵好處是將關注點分離:

  • 檢視只負責獲取一些資料並生成HTML。
  • 應用程式模型只負責執行所需的業務邏輯。
  • 頁面處理程式(控制器)僅負責驗證傳入的請求,並根據應用程式模型的輸出選擇所需的響應。

通過明確定義邊界,可以更容易地更新和測試每個元件,而不依賴其他元件。如果您的UI邏輯發生了變化,您不必修改任何業務邏輯類,因此您不太可能在意外的地方引入錯誤。

緊密耦合的危險
一般來說,儘可能減少應用程式邏輯上分離的部分之間的耦合是一個好主意。這使得更新應用程式更容易,而不會造成不利影響或需要在看似無關的領域進行修改。應用MVC模式是幫助實現這一目標的一種方式。
作為一個例子,我記得幾年前我在開發一個小型網路應用時的一個案例。在匆忙中,我們沒有將我們的業務邏輯與HTML生成程式碼正確地分離,但最初程式碼沒有明顯的問題,所以我們將其交付!
幾個月後,有人開始開發該應用程式,並立即“幫助”重新命名了業務層某個類中的一個無害拼寫錯誤。不幸的是,這些類的名稱被用於生成HTML程式碼,因此重新命名類會導致整個網站在使用者的瀏覽器中崩潰!可以說,在那之後,我們做出了一致的努力來應用MVC模式,並確保我們有適當的關注點分離。

本章所示的示例演示了Razor Pages的大部分功能。它還有其他功能,比如過濾管道,我將在後面介紹(第13章),第6章將更深入地討論繫結模型,但系統的整體行為是相同的。

同樣,在第9章中,我將討論當您使用WebAPI控制器生成機器可讀響應時,MVC設計模式是如何應用的。除產生的最終結果外,該過程在所有意圖和目的上都是相同的。

在下一節中,您將看到如何將RazorPages新增到應用程式中。預設情況下,Visual Studio和.NET CLI中的一些模板將包含Razor Pages,但您將看到如何將其新增到現有應用程式中並探索各種可用選項。

4.1.4 嚮應用程式新增RazorPages

MVC基礎設施,無論是RazorPages還是MVC/API控制器,都是除了最簡單的ASP.NET Core應用程式之外的所有應用程式的基礎,因此幾乎所有模板都包含預設配置的MVC基礎設施。但為了確保您能夠輕鬆地將RazorPages新增到現有專案中,我將向您展示如何從一個基本的空應用程式開始,並從頭開始將Razor Pages新增到此專案中。

你努力的結果還不會令人興奮。我們將在網頁上顯示“Hello World”,但這將顯示將ASP.NET Core應用程式轉換為使用RazorPages是多麼簡單。如果您不需要RazorPages提供的功能,也不必使用它,它還將強調ASP.NET Core的可插拔性。

以下是如何將RazorPages新增到應用程式中:

1. 在Visual Studio 2019中,選擇“檔案”>“新建”>“專案”,或從啟動螢幕中選擇“建立新專案”。
2. 從模板列表中,選擇ASP.NET Core Web應用程式,確保選擇C#語言模板。
3. 在下一個螢幕上,輸入專案名稱、位置和解決方案名稱,然後單擊建立。
4. 在下面的螢幕上,通過在Visual Studio中選擇ASP.NET Core空專案模板,建立一個沒有MVC或RazorPages的基本模板,如圖4.7所示。您可以使用.NET CLI和dotnet new web命令建立一個類似的空專案。

圖4.7 建立空ASP.NET Core模板。空模板將建立一個簡單的ASP.NET Core應用程式,該應用程式包含一個沒有RazorPages的小型中介軟體管道。

5. 在Startup.cs檔案的ConfigureServices方法中新增必要的Razor Page服務(以粗體顯示):

public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
6. 用MapRazorPages()擴充套件方法(粗體)替換中介軟體管道末端EndpointMiddleware中配置的現有基本端點。為了簡單起見,還可以從Startup.cs的Configure方法中刪除現有的錯誤處理程式中介軟體:
public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{
  app.UseRouting();
  app.UseEndpoints(endpoints=>
  {
    endpoints.MapRazorPages();
  });
}
7. 右鍵單擊解決方案資源管理器中的專案,然後選擇“新增”>“新建資料夾”,將新資料夾新增到專案的根目錄中。將新資料夾命名為“Pages”。您現在已經將專案配置為使用RazorPages,但您還沒有任何頁面。以下步驟將新的RazorPage新增到應用程式中。可以使用.NET CLI建立類似的RazorPage,通過從專案目錄執行dotnet new page-n Index -o Pages。
8. 右鍵單擊新頁面資料夾並選擇Add>RazorPage,如圖4.8所示。

圖4.8 向專案新增新的Razor頁面

9. 在下一頁中,選擇RazorPage–Empty,然後單擊Add。在下面的對話方塊中,將頁面命名為Index.cshtml,如圖4.9所示。

圖4.9 使用AddRazorPage對話方塊建立新的Razor頁面

10. Visual Studio完成生成檔案後,開啟Index.cshtml檔案,並更新HTML以表示Hello World!通過將檔案內容替換為以下內容:
@page
@model AddingRazorPagesToEmptyProject.IndexModel
@{
  Layout = null;
}
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width"/>
    <title>Index</title>
  </head>
  <body>
    <h1>Hello World!</h1>
  </body>
</html>

完成所有這些步驟後,您應該能夠恢復、構建和執行應用程式。在Visual Studio 2019中,選擇“檔案”>“新建”>“專案”,或從啟動螢幕中選擇“建立新專案”。

注意:您可以在Visual Studio中按F5鍵(或在專案資料夾的命令列中呼叫dotnet run)來執行專案。這將恢復所有引用的NuGet包,構建專案,並啟動應用程式。Visual Studio將自動開啟瀏覽器視窗以訪問應用程式的主頁。

當您向根“/”路徑發出請求時,應用程式會呼叫IndexModel上的OnGet處理程式,這是由於RazorPages基於檔名的常規路由方式。現在不要擔心這一點;我們將在下一章詳細討論。

OnGet處理程式是一個void方法,它使RazorPage呈現關聯的Razor檢視,並在響應中將其傳送到使用者的瀏覽器。

RazorPages依賴於許多內部服務來執行其功能,這些服務必須在應用程式啟動期間註冊。這是通過呼叫Startup.cs的ConfigureServices方法中的AddRazorPages實現的。如果沒有這一點,你的應用程式啟動時會出現異常,提醒你需要通話。

Configure中對MapRazorPages的呼叫將RazorPage端點註冊到端點中介軟體。作為此呼叫的一部分,將自動註冊用於將URL路徑對映到特定RazorPage處理程式的路由。

注:我將在下一章詳細介紹路由。

本節中的說明描述瞭如何將RazorPages新增到應用程式中,但這並不是將HTML生成新增到應用中的唯一方法。正如我之前提到的,RazorPages構建在ASP.NET Core MVC框架之上,並共享許多相同的概念。在下一節中,我們將簡要介紹MVC控制器,看看它們與RazorPages的比較,並討論何時應該選擇使用一種方法而不是另一種方法。

4.2 Razor Pages與ASP.NET Core中的MVC

在本書中,我將重點放在RazorPages上,因為這已經成為使用ASP.NET Core構建伺服器端渲染應用程式的推薦方法。然而,我還提到Razor Pages在幕後使用ASP.NET Core MVC框架,如果您願意,可以選擇直接使用它。此外,如果您正在建立一個用於處理移動或客戶端應用程式的Web API,那麼您幾乎可以直接使用MVC框架。

注:我在第9章中介紹瞭如何構建Web API。

那麼RazorPages和MVC之間有什麼區別,您應該在什麼時候選擇一個或另一個呢?

如果您是ASP.NET Core的新手,那麼答案很簡單:使用RazorPages來構建伺服器端呈現的應用程式,使用MVC框架來構建WebAPI。我將在後面的章節中討論一些細微差別,但這種區別最初會很好地為您服務。

如果您熟悉ASP.NET的早期版本或ASP.NET Core的早期版本,並且正在決定是否使用Razor Pages,那麼本節將幫助您進行選擇。來自這些背景的開發人員最初常常對RazorPages有誤解(正如我所做的!),錯誤地將其等同於WebForm,忽視了MVC框架的底層基礎。

但是,在我們進行比較之前,我們應該簡要了解一下ASP.NET Core MVC框架本身。瞭解MVC和RazorPages之間的異同非常有用,因為您可能會在某個時候發現MVC的用處,即使您大部分時間都使用RazorPage。

4.2.1 ASP.NET Core中的MVC控制器

在第4.1節中,我們研究了MVC設計模式,以及它如何應用於ASP.NET Core中的RazorPages。也許毫不奇怪,您可以以幾乎完全相同的方式使用ASP.NET Core MVC框架。為了演示RazorPages和MVC之間的區別,我們將檢視清單4.1中RazorPage的MVC版本,其中顯示了給定類別的待辦事項列表。

MVC使用了控制器和操作方法的概念,而不是PageModel和頁面處理程式。如圖4.10所示,它們幾乎直接類似於Razor Pages的計數器部分,圖4.10顯示了與圖4.6等效的MVC。另一方面,MVC控制器使用顯式檢視模型將資料傳遞給Razor檢視,而不是將資料作為屬性本身公開(正如Razor page對頁面模型所做的那樣)。

定義:動作(或動作方法)是響應請求而執行的方法。MVC控制器是一個包含多個邏輯分組的動作方法的類。

圖4.10 完整MVC控制器請求。MVC控制器模式與RazorPages模式幾乎相同,如圖4.6所示。控制器相當於RazorPage,操作相當於頁面處理程式。

清單4.2顯示了一個MVC控制器的示例,它提供了與清單4.1中的RazorPage相同的功能。在MVC中,控制器通常用於將類似的動作聚合在一起,因此這種情況下的控制器稱為ToDoController,因為它通常包含用於處理待辦事項的其他動作方法,例如檢視特定專案或建立新專案的動作。

清單4.2 用於檢視給定類別中所有待辦事項的MVC控制器

public class ToDoController : Controller
{
    private readonly ToDoService _service; 
    public ToDoController(ToDoService service)    //ToDoService是使用依賴注入在控制器建構函式中提供的。
    {
        _service = service;
    }
    public ActionResult Category(string id)    //Category操作方法接受一個引數id。
    {
        var items = _service.GetItemsForCategory(id);    //action方法呼叫ToDoService以檢索資料並構建檢視模型。
        var viewModel = new CategoryViewModel(items);    //檢視模型是一個簡單的C#類,在應用程式的其他地方定義。
        return View(viewModel);    //返回一個ViewResult,指示應渲染Razor檢視,並傳入檢視模型
    }
    //MVC控制器通常包含響應不同請求的多個動作方法。 
    public ActionResult Create(ToDoListModel model)
    {
        // ...
    }
}

除了一些命名上的差異,ToDoController看起來與清單4.1中的Razor Page非常相似。實際上,從架構上講,Razor Pages和MVC本質上是等效的,因為它們都使用MVC設計模式。最明顯的差異與檔案在專案中的位置有關,我將在下一節中討論。

4.2.2 Razor Pages的優勢

在上一節中,我展示了MVC控制器的程式碼看起來與RazorPageModel的程式碼非常相似。如果是這樣的話,使用Razor Pages有什麼好處?在本節中,我將討論MVC控制器的一些痛點,以及RazorPages如何解決這些痛點。

Razor頁面不是Web表單
我從現有ASP.NET開發人員那裡聽到的反對Razor Pages的一個常見論點是“哦,他們只是Web表單。”這種觀點在許多不同的方面都沒有抓住重點,但這是很常見的,值得直接解決。
Web窗體是一種Web程式設計模型,於2002年作為.NET Framework 1.0的一部分發布。它們試圖為首次從桌面開發轉向Web的開發人員提供高效的體驗。
Web表單現在受到了很多詆譭,但它們的弱點後來才變得明顯。Web表單試圖將網路的複雜性隱藏起來,讓您感覺使用桌面應用程式開發。這通常會導致應用程式速度慢,相互依賴性強,難以維護。
Web表單提供了基於頁面的程式設計模型,這就是為什麼Razor Pages有時會與之關聯。然而,正如您所看到的,RazorPages是基於MVC設計模式的,它暴露了web的內在特性,而不試圖隱藏它們。
RazorPages使用約定(您已經看到了其中的一些約定)優化了某些流,但它並不像WebForms那樣試圖在無狀態web應用程式的頂部構建有狀態的應用程式模型。

在MVC中,一個控制器可以有多個動作方法。每個操作處理不同的請求並生成不同的響應。控制器中多個動作的分組有些隨意,但通常用於對與特定實體相關的動作進行分組:在本例中為待辦事項列表項。例如,清單4.2中的ToDoController的更完整版本可能包括列出所有待辦事項、建立新專案和刪除專案的操作方法。不幸的是,您經常會發現您的控制器變得非常龐大和臃腫,具有許多依賴關係。

注:您不必像這樣使控制器非常大。這只是一種常見的模式。例如,您可以為每個操作建立單獨的控制器。

MVC控制器的另一個缺陷是它們在專案中的組織方式。控制器中的大多數操作方法都需要關聯的Razor檢視,以及用於向檢視傳遞資料的檢視模型。MVC方法傳統上按型別(控制器、檢視、檢視模型)對類進行分組,而RazorPage方法按功能對與特定頁面相關的所有內容進行分組。

圖4.11將簡單RazorPages專案的檔案佈局與MVC等效專案進行了比較。使用RazorPages意味著無論何時在處理特定頁面時,都可以在控制器、檢視和檢視模型資料夾之間上下滾動。您所需的一切都在兩個檔案中找到,.cshtml Razor檢視和 .cshtml.cs PageModel檔案。

圖4.11 比較MVC專案的資料夾結構與RazorPages專案的資料夾架構

MVC和RazorPages之間還有其他不同之處,我將在本書中強調,但這種佈局差異確實是很大的益處。RazorPages接受這樣一個事實,即您正在構建基於頁面的應用程式,並通過將與單個頁面相關的所有內容放在一起來優化您的工作流程。

提示:您可以將每個Razor Page視為專注於單個頁面的迷你控制器。頁面處理程式在功能上等同於MVC控制器操作方法。

這種佈局還具有使每個頁面成為單獨的類的優點。這與MVC方法形成了對比,MVC方法將每個頁面作為給定控制器上的操作。每個RazorPage都是針對特定功能的,例如顯示待辦事項。MVC控制器包含處理多個不同功能的動作方法,以實現更抽象的概念,例如與待辦事項相關的所有功能。

另一個重要的點是RazorPages並沒有失去MVC所具有的關注點分離。RazorPages的檢視部分仍然只關注呈現HTML,處理程式是呼叫應用程式模型的協調器。唯一真正的區別是缺少MVC中的顯式檢視模型,但如果這對您來說是一個破壞,那麼在RazorPages中模仿它是完全可能的。

使用Razor Pages的好處在您擁有“內容”網站時尤其明顯,例如營銷網站,在那裡您主要顯示靜態資料,而且沒有真正的邏輯。在這種情況下,MVC增加了複雜性,但沒有任何實際好處,因為控制器中根本沒有任何邏輯。另一個很好的用例是建立表單供使用者提交資料。RazorPages特別針對這個場景進行了優化,您將在後面的章節中看到。

顯然,我是Razor Pages的粉絲,但這並不是說它們適合任何情況。在下一節中,我將討論在應用程式中選擇使用MVC控制器的一些情況。記住,這不是非此即彼的選擇,在同一應用程式中同時使用MVC控制器和RazorPages是可能的,在很多情況下,這可能是最好的選擇。

4.2.3 何時選擇MVC控制器而不是Razor Pages

RazorPages非常適合構建基於頁面的伺服器端渲染應用程式。但並非所有的應用程式都符合這種模式,甚至有些屬於這種型別的應用程式可能最好使用MVC控制器而不是RazorPages來開發。以下是一些這樣的場景:

  • 當您不想呈現檢視時,RazorPages最適合基於頁面的應用程式,在那裡您為使用者呈現檢視。如果您正在構建Web API,則應該使用MVC控制器。
  • 當您將現有MVC應用程式轉換為ASP.NET Core時如果您已經有一個使用MVC的ASP.NET應用程式,那麼將現有MVC控制器轉換為Razor Pages可能不值得。保留現有程式碼更有意義,也許可以考慮使用RazorPages在應用程式中進行新的開發。
  • 當您進行大量部分頁面更新時,可以在應用程式中使用JavaScript,通過一次僅更新部分頁面來避免進行全頁面導航。這種方法介於完全伺服器端渲染和客戶端應用程式之間,使用MVC控制器可能比RazorPages更容易實現。

何時不使用Razor Pages或MVC控制器
通常,您將使用Razor Pages或MVC控制器為應用程式編寫大部分應用程式邏輯。您將使用它來定義應用程式中的API和頁面,並定義它們如何與業務邏輯互動。RazorPages和MVC提供了一個廣泛的框架(您將在接下來的六章中看到),它提供了大量功能,幫助快速高效地構建應用程式。但它們並不適合每個應用。
提供如此多的功能必然會帶來一定程度的效能開銷。對於典型的應用程式,使用MVC或RazorPages帶來的生產力收益遠遠超過了效能影響。但是,如果您正在為雲構建小型、輕量級的應用程式,您可以考慮直接使用定製中介軟體(參見第19章)或gRPC等替代協議(https://docs.microsoft.com/ASP.NET/core/grpc)。你可能還想看看Christian Horsdal Gammelgaard(Manning,2017)的《Microservices in .NET Core》。
或者,如果您正在構建具有實時功能的應用程式,您可能會考慮使用WebSocket而不是傳統的HTTP請求。ASP.NET Core SignalR可以通過在WebSocket上提供抽象來為應用程式新增實時功能。SignalR還提供了簡單的傳輸回退和遠端過程呼叫(RPC)應用程式模型。有關詳細資訊,請參閱文件https://docs.microsoft.com/ASP.NET/core/signalr。
ASP.NET Core 5.0中的另一個選項是Blazor。此框架允許您通過利用WebAssembly標準直接在瀏覽器中執行.NET程式碼,或者使用SignalR的有狀態模型來構建互動式客戶端web應用程式。有關詳細資訊,請參閱文件,網址:https://docs.microsoft.com/ASP.NET/core/blazor/。

希望此時您已在Razor Pages及其整體設計上售出。到目前為止,我們看過的所有RazorPages都使用了一個頁面處理程式。在下一節中,我們將更深入地研究頁面處理程式:如何定義它們,如何呼叫它們,以及如何使用它們來呈現Razor檢視。

4.3 Razor Pages和頁面處理程式

在本章的第一節中,我描述了MVC設計模式及其與ASP.NET Core的關係。在設計模式中,控制器接收請求,並且是UI生成的入口點。對於RazorPages,入口點是駐留在RazorPage的PageModel中的頁面處理程式。頁面處理程式是響應請求而執行的方法。

預設情況下,磁碟上Razor Page的路徑控制Razor頁響應的URL路徑。例如,對URL/products/list的請求對應於路徑pages/products/list.cshtml上的Razor頁面。RazorPages可以包含任意數量的頁面處理程式,但只有一個頁面處理程式響應給定的請求執行。

注意:在下一章中,您將瞭解有關選擇RazorPage和處理程式(稱為路由)的更多資訊。

頁面處理程式的職責通常有三重:

  • 確認傳入請求有效。
  • 呼叫與傳入請求相對應的適當業務邏輯。
  • 選擇要返回的適當型別的響應。

頁面處理程式不需要執行所有這些操作,但至少它必須選擇要返回的響應型別。頁面處理程式通常返回以下三項之一:

  • PageResult物件——這將導致關聯的Razor檢視生成HTML響應。
  • Nothing(處理程式返回void或Task)——這與前面的情況相同,導致Razor檢視生成HTML響應。
  • RedirectToPageResult——這表示應該將使用者重定向到應用程式中的其他頁面。

這些是Razor Pages最常用的結果,但我在第4.3.2節中描述了一些其他選項。

重要的是要認識到頁面處理程式不會直接生成響應;它選擇響應型別併為其準備資料。例如,返回PageResult時不會生成任何HTML;它僅僅指示應當呈現檢視。這與MVC設計模式保持一致,其中生成響應的是檢視,而不是控制器。

提示:頁面處理程式負責選擇要傳送的響應型別;MVC框架中的檢視引擎使用結果來生成響應。

還值得記住的是,頁面處理程式通常不應該直接執行業務邏輯。相反,他們應該在應用程式模型中呼叫適當的服務來處理請求。例如,如果頁面處理程式收到將產品新增到使用者購物車的請求,則不應直接操作資料庫或重新計算購物車總數。相反,它應該呼叫另一個類來處理細節。這種分離關注點的方法確保了程式碼在增長時保持可測試性和可維護性。

4.3.1 接受頁面處理程式的引數

向頁面處理程式發出的某些請求將需要附加值以及有關請求的詳細資訊。如果請求是搜尋頁面,則請求可能包含搜尋詞的詳細資訊和他們正在檢視的頁碼。如果請求嚮應用程式釋出表單,例如使用者使用使用者名稱和密碼登入,則這些值必須包含在請求中。在其他情況下,將沒有值,例如當用戶請求應用程式的主頁時。

請求可能包含來自各種不同來源的附加值。它們可以是URL、查詢字串、標頭或請求本身的一部分。中介軟體將從每個源中提取值,並將它們轉換為.NET型別。

定義:從請求中提取值並將其轉換為.NET型別的過程稱為模型繫結。我在第6章中討論了模型繫結。

ASP.NET Core可以在Razor Pages中繫結兩個不同的目標:

  • 方法引數——如果頁面處理程式具有方法引數,則使用請求中的值來建立所需的引數。
  • 用[BindProperty]屬性標記的屬性——將繫結用該屬性標記的任何屬性。預設情況下,此屬性對GET請求不起作用。

模型繫結值可以是簡單型別,例如字串和整數,也可以是複雜型別,如以下列表所示。如果請求中提供的任何值未繫結到屬性或頁面處理程式引數,則其他值將不使用。

清單4.3 Razor頁面處理程式示例

public class SearchModel : PageModel
{
    private readonly SearchService _searchService; 
    //SearchService提供給SearchModel以用於頁面處理程式。
    public SearchModel(SearchService searchService)
    {
        _searchService = searchService;
    }
    
    [BindProperty]
    public BindingModel Input { get; set; }     //用[BindProperty]屬性修飾的屬性將被模型繫結。
    public List<Product> Results { get; set; }    //未修飾的屬性將不會繫結到模型。
    
    //頁面處理程式不需要檢查模型是否有效。返回void將渲染檢視。
    public void OnGet()
    {
    }
    
    public IActionResult OnPost(int max)    //此頁面處理程式中的max引數將使用請求中的值進行模型繫結。
    {
        //如果請求無效,則該方法指示應將使用者重定向到索引頁。 
        if (ModelState.IsValid)
        {
            Results = _searchService.Search (Input.SearchTerm, max); 
            return Page();
        }
        return RedirectToPage("./Index");
    }
}

在本例中,OnGet處理程式不需要任何引數,而且方法很簡單,它返回void,這意味著將呈現關聯的Razor檢視。它還可能返回PageResult;效果也會是一樣的。請注意,此處理程式用於HTTP GET請求,因此未繫結用[BindProperty]修飾的Input屬性。

提示:要繫結GET請求的屬性,請使用屬性的SupportsGet屬性;例如[BindProperty(SupportsGet=true)]。

相反,OnPost處理程式接受引數max作為引數。在本例中,它是一個簡單的型別int,但也可以是一個複雜的物件。此外,由於此處理程式對應於HTTP POST請求,因此Input屬性也被模型繫結到請求。

注意:與大多數.NET類不同,您不能使用方法過載在RazorPage上使用相同名稱的多個頁面處理程式。

當操作方法使用模型繫結屬性或引數時,應始終使用ModelState.IsValid檢查所提供的模型是否有效。ModelState屬性作為基PageModel類上的屬性公開,可用於檢查所有繫結的屬性和引數是否有效。當您瞭解驗證時,您將在第6章中看到該過程是如何工作的。

一旦頁面處理程式確定提供給操作的方法引數有效,它就可以執行適當的業務邏輯並處理請求。對於OnPost處理程式,這涉及呼叫提供的SearchService並在Results屬性上設定結果。最後,處理程式通過呼叫基方法返回PageResult

return Page();

如果模型無效,則沒有任何結果可顯示!在此示例中,該操作使用RedirectToPage幫助器方法返回RedirectToPageResult。執行時,此結果將向用戶傳送302重定向響應,這將導致其瀏覽器導航到Index Razor Page。

注意,OnGet方法在方法簽名中返回void,而OnPost方法返回IActionResult。這在OnPost方法中是必需的,以便允許C#編譯(因為Page和RedirectToPage幫助器方法返回不同的型別),但它不會改變方法的最終行為。您可以很容易地在OnGet方法中呼叫Page並返回一個IActionResult,其行為將是相同的。

提示:如果要從頁面處理程式返回多個型別的結果,則需要確保方法返回IActionResult。

在下一節中,我們將更深入地瞭解操作結果及其用途。

4.3.2 返回帶有ActionResults的響應

在上一節中,我強調了頁面處理程式決定返回什麼型別的響應,但它們自己不會生成響應。它是頁面處理程式返回的IActionResult,當RazorPages基礎結構使用檢視引擎執行該處理程式時,將生成響應。

這種方法是遵循MVC設計模式的關鍵。它將傳送何種響應的決定與響應的生成分開。這允許您輕鬆測試動作方法邏輯,以確認為給定輸入傳送了正確型別的響應。例如,您可以單獨測試給定的IActionResult是否生成預期的HTML。

ASP.NET Core有許多不同型別的IActionResult:

  • PageResult——為Razor Pages中的關聯頁面生成HTML檢視
  • ViewResult——使用MVC控制器時為給定Razor檢視生成HTML檢視
  • RedirectToPageResult——傳送302 HTTP重定向響應以自動將使用者傳送到另一個頁面
  • RedirectResult——傳送302 HTTP重定向響應,自動將使用者傳送到指定的URL(不必是Razor Page)
  • FileResult——返回檔案作為響應
  • ContentResult——返回提供的字串作為響應
  • StatusCodeResult——傳送原始HTTP狀態程式碼作為響應,可以選擇與相關的響應正文內容
  • NotFoundResult——傳送原始404 HTTP狀態程式碼作為響應

當RazorPages執行這些命令時,將生成一個響應,通過中介軟體管道傳送回用戶。

提示:當您使用Razor Pages時,通常不會使用其中的一些操作結果,例如ContentResult和StatusCodeResult。不過,瞭解它們是很好的,因為如果您使用MVC控制器構建Web API,您可能會使用它們。

在本節中,我將簡要介紹RazorPages中使用的最常見的IActionResult類。

PageResult和RedirectToPageResult

當您使用RazorPages構建傳統的web應用程式時,通常會使用PageResult,後者使用Razor生成HTML響應。我們將在第7章中詳細瞭解這是如何發生的。

您還通常使用各種基於重定向的結果將使用者傳送到新的網頁。例如,當您在電子商務網站上下單時,通常會瀏覽多個頁面,如圖4.12所示。每當需要您移動到其他頁面時,例如當用戶提交表單時,web應用程式就會發送HTTP重定向。您的瀏覽器會自動跟蹤重定向請求,從而在結賬過程中建立無縫流程。

圖4.12 通過網站的典型POST、REDIRECT和GET流程。使用者將購物籃傳送到結賬頁面,該頁面驗證其內容並重定向到付款頁面,而無需使用者手動更改URL。

在這個流程中,每當您返回HTML時,都會使用PageResult;重定向到新頁面時,使用RedirectToPageResult。

提示:RazorPages通常被設計為無狀態的,因此如果您想在多個頁面之間持久化資料,則需要將其放置在資料庫或類似的儲存中。如果您只想為單個請求儲存資料,您可以使用TempData,它為單個請求在cookie中儲存少量資料。有關詳細資訊,請參閱文件:http://mng.bz/XdXp。

未找到結果和狀態搜尋結果

除了HTML和重定向響應之外,您有時還需要傳送特定的HTTP狀態程式碼。如果您請求在電子商務應用程式上檢視產品的頁面,而該產品不存在,則會向瀏覽器返回404 HTTP狀態程式碼,您通常會看到“未找到”網頁。RazorPages可以通過返回NotFoundResult來實現此行為,該結果將返回原始的404 HTTP狀態程式碼。使用StatusCodeResult並將顯式返回的狀態程式碼設定為404,可以獲得類似的結果。

注意:NotFoundResult不會生成任何HTML;它只生成一個原始的404狀態程式碼,並通過中介軟體管道返回它。但是,如前一章所述,您可以使用StatusCodePagesMiddleware在生成原始404狀態程式碼後攔截該程式碼,併為其提供使用者友好的HTML響應。

使用助手方法建立ActionResult類

可以使用C#的普通新語法建立和返回ActionResult類:

return new PageResult()

然而,RazorPagesPageModel基類還提供了許多用於生成響應的幫助器方法。通常使用Page方法生成適當的PageResult,使用RedirectToPage方法生成RedirectToPageResult,或使用NotFound方法生成NotFoundResult。

提示:大多數ActionResult類在基本PageModel類上都有一個helper方法。它們通常命名為Type,生成的結果稱為Type-result。例如,StatusCode方法返回StatusCodeResult例項。

如前所述,返回IActionResult的行為不會立即生成響應,而是RazorPages基礎結構執行IActionResult,這發生在action方法之外。生成響應後,RazorPages將其返回到中介軟體管道。從那裡,它通過管道中所有註冊的中介軟體,然後ASP.NET Core web伺服器最終將其傳送給使用者。

現在,您應該全面瞭解MVC設計模式,以及它與ASP.NET Core和RazorPages的關係。RazorPage上的頁面處理程式方法是響應給定請求而呼叫的,用於通過返回IActionResult來選擇要生成的響應型別。

重要的是要記住,ASP.NET Core中的MVC和Razor Pages基礎架構作為EndpointMiddleware管道的一部分執行,正如您在上一章中所看到的。生成的任何響應,無論是PageResult還是RedirectToPageResult,都將通過中介軟體管道傳回,從而為中介軟體提供了一個潛在的機會,以便在web伺服器將響應傳送給使用者之前觀察該響應。

我只模糊地談到了RoutingMiddleware如何決定為給定請求呼叫哪個RazorPage和處理程式。你不希望應用程式中的每個URL都有Razor頁面。例如,在電子商店中,每個產品都很難有不同的頁面——每個產品都需要自己的Razor頁面!處理這個和其他場景是路由基礎設施的作用,也是ASP.NET Core的關鍵部分。在下一章中,您將看到如何定義路由,如何向路由新增約束,以及如何解構URL以匹配單個Razor Page處理程式。

總結

  • MVC設計模式允許在應用程式的業務邏輯、傳遞的資料和響應中的資料顯示之間分離關注點。
  • RazorPages是基於ASP.NET Core MVC框架構建的,它們使用許多相同的原語。他們使用約定和不同的專案佈局來優化基於頁面的場景。
  • MVC控制器包含多個動作方法,通常圍繞一個高階實體分組。RazorPages將單個頁面的所有頁面處理程式分組在一個位置,圍繞頁面/功能而不是實體分組。
  • 每個RazorPage相當於一個專注於單個頁面的小型控制器,每個Razor Page處理程式對應於一個單獨的操作方法。
  • RazorPages應該繼承自PageModel基類。
  • 在一個稱為路由的過程中,根據傳入請求的URL、HTTP謂詞和請求的查詢字串選擇一個RazorPage處理程式。
  • 頁面處理程式通常應該委託給服務來處理請求所需的業務邏輯,而不是自己執行更改。這確保了清晰的關注點分離,有助於測試並改進應用程式結構。
  • 頁面處理程式可以具有引數,這些引數的值取自稱為模型繫結的程序中傳入請求的屬性。用[BindProperty]修飾的屬性也可以繫結到請求。
  • 預設情況下,用[BindProperty]修飾的屬性不繫結GET請求。要啟用繫結,請使用[BindProperty(SupportsGet=true)]。
  • 頁面處理程式可以返回PageResult或void以生成HTML響應。
  • 您可以使用RedirectToPageResult將使用者傳送到新的Razor頁面。
  • PageModel基類公開了許多用於建立ActionResult的幫助器方法。