原文地址:Introduction to Razor Pages in ASP.NET Core 譯文地址:介紹 asp.net core 中的 Razor Pages 翻譯:ganqiyinhtml
Razor Pages 是 ASP.NET Core MVC 的一個新功能,可讓基於頁面的編程方案更容易,更高效。git
若是您正在尋找使用 Model-View-Controller 的教程,請參閱ASP.NET Core MVC入門。github
安裝 .NET Core 2.0.0 或更高版本.web
若是您使用 Visual Studio 工具, 請安裝具備如下工做組件的 Visual Studio 15.3 或更高版本:編程
有關如何使用Visual Studio建立 Razor Pages 項目的詳細說明,請參閱「 Razor Pages入門」。visual-studio-code
從命令行運行 dotnet new razor
。api
從 Visual Studio for Mac 打開生成的 .csproj 文件。瀏覽器
從命令行運行 dotnet new razor
。服務器
從命令行運行 dotnet new razor
。mvc
Razor Pages 在 Startup.cs 啓用:
public class Startup { public void ConfigureServices(IServiceCollection services) { // Includes support for Razor Pages and controllers. services.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseMvc(); } }
@page <h1>Hello, world!</h1> <h2>The time on the server is @DateTime.Now</h2>
前面的代碼看起來很像Razor視圖文件。 @page
指令使它不同凡響. @page
讓文件變成一個 MVC action - 這意味着它能夠直接處理請求,而無需經過控制器。 @page
必須是頁面上的第一個Razor指令。 @page
會影響其餘Razor結構的行爲。 @functions 指令啓用 function-level 內容。
如下兩個文件中顯示了一個相似的頁面,其中 PageModel
位於單獨的文件中。 Pages/Index2.cshtml 文件:
@page @using RazorPages @model IndexModel2 <h2>Separate page model</h2> <p> @Model.Message </p>
Pages/Index2.cshtml.cs '代碼後置' 文件:
using Microsoft.AspNetCore.Mvc.RazorPages; using System; namespace RazorPages { public class IndexModel2 : PageModel { public string Message { get; private set; } = "PageModel in C#"; public void OnGet() { Message += $" Server time is { DateTime.Now }"; } } }
根據約定, PageModel
class 文件與附加了 .cs 的 Razor Page 文件具備相同名稱。例如, 代碼前置的 Razor Page 是 Pages/Index2.cshtml。 那麼包含 PageModel
class 的代碼後置文件名稱就是 Pages/Index2.cshtml.cs。
對於簡單的頁面來講, PageModel
class 與 Razor 標記混合使用是很好的選擇. 對於更復雜的代碼來講, 最好的作法是保持頁面與代碼分離.
網頁路徑到頁面的關聯由頁面在文件系統中的位置肯定。 下表顯示了Razor Page路徑和匹配的URL:
文件名稱和路徑 | 匹配的 URL |
---|---|
/Pages/Index.cshtml | / or /Index |
/Pages/Contact.cshtml | /Contact |
/Pages/Store/Contact.cshtml | /Store/Contact |
/Pages/Store/Index.cshtml | /Store or /Store/Index |
備註:
Index
是默認頁面。Razor Pages 功能旨在使與Web瀏覽器一塊兒使用的常見模式變得容易(Razor Pages features are designed to make common patterns used with web browsers easy)。 Model 綁定, Tag Helpers, and HTML helpers 均可以使用 Razor Page 中的默認屬性讓它們一塊兒工做 。 研究一個實現了基礎的「聯繫咱們」 form 的頁面的 Contact
模型:
對於本文檔中的示例, DbContext
在 Startup.cs 文件中初始化。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using RazorPagesContacts.Data; namespace RazorPagesContacts { public class Startup { public IHostingEnvironment HostingEnvironment { get; } public void ConfigureServices(IServiceCollection services) { services.AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase("name")); services.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseMvc(); } } }
數據模型:
using System.ComponentModel.DataAnnotations; namespace RazorPagesContacts.Data { public class Customer { public int Id { get; set; } [Required, StringLength(100)] public string Name { get; set; } } }
Pages/Create.cshtml 視圖文件:
@page @model RazorPagesContacts.Pages.CreateModel @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers <html> <body> <p> Enter your name. </p> <div asp-validation-summary="All"></div> <form method="POST"> <div>Name: <input asp-for="Customer.Name" /></div> <input type="submit" /> </form> </body> </html>
Pages/Create.cshtml.cs 視圖文件的後置代碼文件:
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using RazorPagesContacts.Data; namespace RazorPagesContacts.Pages { public class CreateModel : PageModel { private readonly AppDbContext _db; public CreateModel(AppDbContext db) { _db = db; } [BindProperty] public Customer Customer { get; set; } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _db.Customers.Add(Customer); await _db.SaveChangesAsync(); return RedirectToPage("/Index"); } } }
根據約定, PageModel
class 被稱爲 <PageName>Model
與該頁面在同一個命名空間中。在頁面中使用 @functions
定義處理程序和使用 PageModel
class 不須要太多的改變。
使用 PageModel
後置代碼文件支持單元測試, 但要求您編寫一個含有顯示的構造函數的類。 沒有 PageModel
後置代碼文件的頁面支持運行時編譯,這在開發中多是一個優點.
該頁面包含一個在 POST
請求(用戶提交表單時)上運行的 OnPostAsync
方法。 您能夠爲任何 HTTP 請求添加方法。 最多見的處理方法有:
OnGet
初始化頁面所須要的狀態. OnGet 示例。OnPost
處理表單提交。Async
命名後綴是可選的,但一般用於標識異步方法。 前面示例中的 OnPostAsync
代碼一般是寫在一個 Controller 中。 上述代碼是一個典型的 Razor Pages 代碼。 大多數 MVC 原始預防,如 model 綁定, 驗證, 和 Action 結果都是共享的。
前面的 OnPostAsync
方法:
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _db.Customers.Add(Customer); await _db.SaveChangesAsync(); return RedirectToPage("/Index"); }
OnPostAsync
的基本流程:
檢查驗證錯誤。
單輸入的數據校驗經過後, OnPostAsync
方法會調用 RedirectToPage
幫助方法來返回一個 RedirectToPageResult
實例. RedirectToPage
是一個新的 action 結果, 相似於 RedirectToAction
或 RedirectToRoute
,可是它是爲 pages 定製的。 在上面的示例中, 它重定向到根目錄 Index 頁面 (/Index
). RedirectToPage
在 頁面的URL生成(URL generation for Pages) 部分中有詳細說明。
當提交的表單具備驗證錯誤(傳遞給服務器)時,OnPostAsync
方法會調用 Page
方法. Page
返回一個 PageResult
實例。 返回 Page
相似於在 controllers 中執行返回 View
方法。 PageResult
是處理程序方法的默認 返回類型。一個返回 void
來呈現頁面的方法。
自定義
屬性使用 [BindProperty]
特性來處理 model 綁定。
public class CreateModel : PageModel { private readonly AppDbContext _db; public CreateModel(AppDbContext db) { _db = db; } [BindProperty] public Customer Customer { get; set; } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _db.Customers.Add(Customer); await _db.SaveChangesAsync(); return RedirectToPage("/Index"); } }
一般狀況下,Razor Pages 僅在 non-GET 方法中使用屬性綁定。 屬性綁定能夠減小您編寫的代碼量。綁定經過使用相同的屬性來呈現表單字段 (<input asp-for="Customer.Name" />
) 和接受輸入,因此能夠減小代碼量。
主頁 (Index.cshtml):
@page @model RazorPagesContacts.Pages.IndexModel @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers <h1>Contacts</h1> <form method="post"> <table class="table"> <thead> <tr> <th>ID</th> <th>Name</th> </tr> </thead> <tbody> @foreach (var contact in Model.Customers) { <tr> <td>@contact.Id</td> <td>@contact.Name</td> <td> <a asp-page="./Edit" asp-route-id="@contact.Id">edit</a> <button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button> </td> </tr> } </tbody> </table> <a asp-page="./Create">Create</a> </form>
後置代碼文件 Index.cshtml.cs :
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using RazorPagesContacts.Data; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; namespace RazorPagesContacts.Pages { public class IndexModel : PageModel { private readonly AppDbContext _db; public IndexModel(AppDbContext db) { _db = db; } public IList<Customer> Customers { get; private set; } public async Task OnGetAsync() { Customers = await _db.Customers.AsNoTracking().ToListAsync(); } public async Task<IActionResult> OnPostDeleteAsync(int id) { var contact = await _db.Customers.FindAsync(id); if (contact != null) { _db.Customers.Remove(contact); await _db.SaveChangesAsync(); } return RedirectToPage(); } } }
Index.cshtml 文件包含一下標記,用於爲每一個節點建立一個編輯連接:
<a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>
Anchor Tag Helper(錨標記輔助) 使用 asp-route-{value} 屬性來生成一個能夠跳轉到編輯頁面的連接。 該連接包含 contact ID 路由數據。 例如, http://localhost:5000/Edit/1
.
Pages/Edit.cshtml 文件:
@page "{id:int}" @model RazorPagesContacts.Pages.EditModel @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ ViewData["Title"] = "Edit Customer"; } <h1>Edit Customer - @Model.Customer.Id</h1> <form method="post"> <div asp-validation-summary="All"></div> <input asp-for="Customer.Id" type="hidden" /> <div> <label asp-for="Customer.Name"></label> <div> <input asp-for="Customer.Name" /> <span asp-validation-for="Customer.Name" ></span> </div> </div> <div> <button type="submit">Save</button> </div> </form>
第一行包含 @page "{id:int}"
指令。 路由約束 "{id:int}"
告訴頁面接受對包含 int
路由數據的頁面的請求。 若是對頁面的請求不包含可轉換爲 int
的路由數據,則運行時返回HTTP 404(未找到)錯誤。
Pages/Edit.cshtml.cs 文件:
using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using RazorPagesContacts.Data; namespace RazorPagesContacts.Pages { public class EditModel : PageModel { private readonly AppDbContext _db; public EditModel(AppDbContext db) { _db = db; } [BindProperty] public Customer Customer { get; set; } public async Task<IActionResult> OnGetAsync(int id) { Customer = await _db.Customers.FindAsync(id); if (Customer == null) { return RedirectToPage("/Index"); } return Page(); } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _db.Attach(Customer).State = EntityState.Modified; try { await _db.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { throw new Exception($"Customer {Customer.Id} not found!"); } return RedirectToPage("/Index"); } } }
您沒必要爲防僞驗證編寫任何代碼。 Razor Pages自動包含防僞令牌生成和驗證。
全部適用於頁面上工做的 Razor 視圖引擎的功能: Layouts,partials,templates,Tag Helpers,_ViewStart.cshtml, _ViewImports.cshtml 的工做方式與傳統Razor視圖相同。
讓咱們經過其中的一些特性來梳理一下該頁面吧。
添加一個 佈局(layout)頁面 Pages/_Layout.cshtml:
<!DOCTYPE html> <html> <head> <title>Razor Pages Sample</title> </head> <body> <a asp-page="/Index">Home</a> @RenderBody() <a asp-page="/Customers/Create">Create</a> <br /> </body> </html>
佈局:
更多信息請查看 layout page。
在 Pages/_ViewStart.cshtml 中設置 Layout 屬性:
@{ Layout = "_Layout"; }
注意: 佈局位於 Pages 文件夾中。分層查找其餘視圖頁面 (layouts, templates, partials) 的起點是當前頁面所在的文件夾。在 Pages 文件夾中的佈局頁能夠被位於 Pages 文件夾下的任意 Razor 頁面使用。
咱們建議您不要把佈局文件放在 Views/Shared 文件夾下。 Views/Shared 是一個 MVC 視圖模式。 Razor Pages 依賴於文件夾層次結構,而不是路徑約定。
來自 Razor 頁面的視圖查找包括 Pages 文件夾。您使用MVC控制器和常規Razor視圖的佈局,模板和部分視圖能夠正常工做(View search from a Razor Page includes the Pages folder. The layouts, templates, and partials you're using with MVC controllers and conventional Razor views just work.)。
添加一個 Pages/_ViewImports.cshtml 文件:
@namespace RazorPagesContacts.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace
將在本教程的後面介紹。 @addTagHelper
指令將內置的 built-in Tag Helpers 引入 Pages 文件夾中的全部頁面。
當 @namespace
指令在頁面上顯式使用時:
@page @namespace RazorPagesIntro.Pages.Customers @model NameSpaceModel <h2>Name space</h2> <p> @Model.Message </p>
該指令用於在當前頁面中設置命名空間(namespace) 。 @model
僞指令能夠不用包含命名空間。
當 @namespace
指令包含在 _ViewImports.cshtml 中時, @namespace
指令指定的命名空間爲須要導入有相同命名空間的頁面提供了生成命名空間的前綴。 生成命名空間 (後綴部分) 的其他部分則在包含了 _ViewImports.cshtml 文件的文件夾中的頁面中。
例如, Pages/Customers/Edit.cshtml.cs 代碼後置文件中顯示的設置了命名空間:
namespace RazorPagesContacts.Pages { public class EditModel : PageModel { private readonly AppDbContext _db; public EditModel(AppDbContext db) { _db = db; } // Code removed for brevity.
Pages/_ViewImports.cshtml 文件設置如下命名空間:
@namespace RazorPagesContacts.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Pages/Customers/Edit.cshtml Razor 頁面中引用的命名空間與代碼後置文件中的命名空間相同。 @namespace
指令被設計成能同時在 C# 項目類中和頁面代碼後置文件中使用,而不須要使用 @using
指令。
注意: @namespace
一樣適用於常規 Razor 視圖。
原始的 Pages/Create.cshtml 視圖文件:
@page @model RazorPagesContacts.Pages.CreateModel @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers <html> <body> <p> Enter your name. </p> <div asp-validation-summary="All"></div> <form method="POST"> <div>Name: <input asp-for="Customer.Name" /></div> <input type="submit" /> </form> </body> </html>
更新後的頁面:
The Pages/Create.cshtml view file:
@page @model CreateModel <html> <body> <p> Enter your name. </p> <div asp-validation-summary="All"></div> <form method="POST"> <div>Name: <input asp-for="Customer.Name" /></div> <input type="submit" /> </form> </body> </html>
Razor Pages 項目示例 包含配置客戶端驗證的鉤子 Pages/_ValidationScriptsPartial.cshtml。
Create
頁面中使用 RedirectToPage
來跳轉上一頁:
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _db.Customers.Add(Customer); await _db.SaveChangesAsync(); return RedirectToPage("/Index"); }
這款應用有如下「文件/文件夾」結構
/Pages
Index.cshtml
/Customer
成功後,Pages/Customers/Create.cshtml 和 Pages/Customers/Edit.cshtml 頁跳轉到 Pages/Index.cshtml 頁。 /Index
是跳轉到前一頁的 URI 的一部分。 /Index
可用於生成 Pages/Index.cshtml 頁面的 URI。例如:
Url.Page("/Index", ...)
<a asp-page="/Index">My Index Page</a>
RedirectToPage("/Index")
頁面名稱是從 /Pages 根文件夾(包括 前導 /
,例如 /Index
)到頁面的路徑。上述示例展現了更靈活的URL生成功能,而不只僅是對URL進行硬編碼。使用路由 生成URL,並能夠根據自定義的路由在目標路徑中生成和編碼參數。
支持相對名稱生成頁面的 URL 。 下表展現了在 Pages/Customers/Create.cshtml 中調用 RedirectToPage
方法生成跳轉到不一樣 Index 頁面的方式(使用了不一樣的參數):
RedirectToPage(x) | Page |
---|---|
RedirectToPage("/Index") | Pages/Index |
RedirectToPage("./Index"); | Pages/Customers/Index |
RedirectToPage("../Index") | Pages/Index |
RedirectToPage("Index") | Pages/Customers/Index |
RedirectToPage("Index")
, RedirectToPage("./Index")
, 和 RedirectToPage("../Index")
都使用了 相對名稱。 RedirectToPage
的參數表示 參考 當前頁面的路徑計算獲得目標頁面的名稱。 <!-- 評論:原始提供的字符串與當前頁面的頁面名稱組合以計算目標頁面的名稱。 -- 頁面名稱,而不是頁面路徑 -->
構建具備複雜結構的站點時,相對名稱連接頗有用。 若是使用相對名稱在文件夾中的頁面之間連接,則能夠重命名該文件夾。 全部連接仍然有效(由於它們不包括文件夾名稱)。
ASP.NET Core 公開了 controller 上的 TempData 屬性。 此屬性存儲數據,直到它被讀取。Keep
和 Peek
方法可用於在不刪除數據的狀況下檢查數據。當數據被多個請求須要的時候, TempData
對重定向頗有用。
[TempData]
在 ASP.NET Core 2.0 中是一個新的屬性,且 controllers 和 pages 都支持這個屬性。
如下代碼就是使用 TempData
來傳遞 Message
的值。
public class CreateDotModel : PageModel { private readonly AppDbContext _db; public CreateDotModel(AppDbContext db) { _db = db; } [TempData] public string Message { get; set; } [BindProperty] public Customer Customer { get; set; } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _db.Customers.Add(Customer); await _db.SaveChangesAsync(); Message = $"Customer {Customer.Name} added"; return RedirectToPage("./Index"); } }
如下在 Pages/Customers/Index.cshtml 文件中的標記顯示了使用 TempData
存儲的 Message
的值。
<h3>Msg: @Model.Message</h3>
Pages/Customers/Index.cshtml.cs 後置代碼文件將 [TempData]
屬性應用到 Message
屬性。
[TempData] public string Message { get; set; }
更多信息請查看 TempData 。
如下頁面使用 asp-page-handler
Tag Helper 爲頁面生成兩個處理程序標記:
@page @model CreateFATHModel <html> <body> <p> Enter your name. </p> <div asp-validation-summary="All"></div> <form method="POST"> <div>Name: <input asp-for="Customer.Name" /></div> <input type="submit" asp-page-handler="JoinList" value="Join" /> <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" /> </form> </body> </html>
上述示例中的表單有兩個提交按鈕,每一個都使用 FormActionTagHelper
提交到一個不一樣的 URL。asp-page-handler
屬性跟隨着 asp-page
。asp-page-handler
生成頁面須要提交的方法事件的 URL。 沒有提到 asp-page
是由於示例是連接到當前頁面的。
後置代碼文件:
using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using RazorPagesContacts.Data; namespace RazorPagesContacts.Pages.Customers { public class CreateFATHModel : PageModel { private readonly AppDbContext _db; public CreateFATHModel(AppDbContext db) { _db = db; } [BindProperty] public Customer Customer { get; set; } public async Task<IActionResult> OnPostJoinListAsync() { if (!ModelState.IsValid) { return Page(); } _db.Customers.Add(Customer); await _db.SaveChangesAsync(); return RedirectToPage("/Index"); } public async Task<IActionResult> OnPostJoinListUCAsync() { if (!ModelState.IsValid) { return Page(); } Customer.Name = Customer.Name?.ToUpper(); return await OnPostJoinListAsync(); } } }
上訴代碼使用了 named handler methods. Named handler methods 是經過在 On<HTTP Verb>
和 Async
(若是存在) 之間的文本建立的。 在前面的示例中, 頁面方法是 OnPostJoinListAsync 和 OnPostJoinListUCAsync。移除 OnPost 和 Async 後,處理程式的名字是 JoinList
和 JoinListUC
。
<input type="submit" asp-page-handler="JoinList" value="Join" /> <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
使用上述代碼,提交到 OnPostJoinListAsync
的 URL 是 http://localhost:5000/Customers/CreateFATH?handler=JoinList
. 提交到 OnPostJoinListUCAsync
的 URL 是 http://localhost:5000/Customers/CreateFATH?handler=JoinListUC
.
若是您不喜歡 URL 中的查詢字符串 ?handler=JoinList
,則能夠更改路由以將處理名稱加入到 URL 中。您能夠經過在 @page
指令以後添加用雙引號括起來的路由模板來自定義路由。
@page "{handler?}" @model CreateRouteModel <html> <body> <p> Enter your name. </p> <div asp-validation-summary="All"></div> <form method="POST"> <div>Name: <input asp-for="Customer.Name" /></div> <input type="submit" asp-page-handler="JoinList" value="Join" /> <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" /> </form> </body> </html>
前面的路由將處理名稱放入 URL 中,用於替代查詢字符串。那 ?
後面的 handler
意味着 route 參數時時可選的。
您可使用 @page
將額外的部分和參數添加到頁面的路由中。不管頁面的默認路由附加了什麼,都不支持使用絕對或虛擬路徑來更改頁面的路由(好比 "~/Some/Other/Path"
) 。
須要配置高級選項,請在 MVC 構建器上使用擴展方法AddRazorPagesOptions
:
public void ConfigureServices(IServiceCollection services) { services.AddMvc() .AddRazorPagesOptions(options => { options.RootDirectory = "/MyPages"; options.Conventions.AuthorizeFolder("/MyPages/Admin"); }); }
目前,您可使用 RazorPagesOptions
設置頁面的根目錄,或爲頁面添加應用程序模型約定。 咱們但願從此可以實現更多的擴展性。
要預編譯視圖,請參閱 Razor view compilation 視圖編譯。
請參閱 ASP.NET Core 的 Razor Pages 入門。