Razor 頁(yè)面是 ASP.NET Core MVC 的一個(gè)新功能,它可以使基于頁(yè)面的編碼方式更簡(jiǎn)單高效。
若要查找使用模型視圖控制器方法的教程,請(qǐng)參閱 ASP.NET Core MVC 入門。
本文檔介紹 Razor 頁(yè)面。 它并不是分步教程。 如果認(rèn)為某些部分過(guò)于復(fù)雜,請(qǐng)參閱 Razor 頁(yè)面入門。 有關(guān) ASP.NET Core 的概述,請(qǐng)參閱 ASP.NET Core 簡(jiǎn)介。
系統(tǒng)必備
Install one of the following:
- CLI tooling: Windows, Linux, or macOS: .NET Core SDK 2.0 or later
- IDE/editor tooling
- Windows: Visual Studio for Windows
- ASP.NET and web development workload
- .NET Core cross-platform development workload
- Linux: Visual Studio Code
- macOS: Visual Studio for Mac
- Windows: Visual Studio for Windows
創(chuàng)建 Razor 頁(yè)面項(xiàng)目
Visual Studio
請(qǐng)參閱 Razor 頁(yè)面入門,獲取關(guān)于如何使用 Visual Studio 創(chuàng)建 Razor 頁(yè)面項(xiàng)目的詳細(xì)說(shuō)明。
Visual Studio for Mac
在命令行中運(yùn)行dotnet new razor。
在 Visual Studio for Mac 中打開生成的 .csproj 文件。
Visual Studio Code
在命令行中運(yùn)行dotnet new razor。
.NET Core CLI
在命令行中運(yùn)行dotnet new razor。
Razor 頁(yè)面
Startup.cs 中已啟用 Razor 頁(yè)面:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Includes support for Razor Pages and controllers.
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
}
}
請(qǐng)考慮一個(gè)基本頁(yè)面:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
上述代碼看上去類似于一個(gè) Razor 視圖文件。 不同之處在于 @page 指令。 @page 使文件轉(zhuǎn)換為一個(gè) MVC 操作 ,這意味著它將直接處理請(qǐng)求,而無(wú)需通過(guò)控制器處理。 @page 必須是頁(yè)面上的第一個(gè) Razor 指令。 @page 將影響其他 Razor 構(gòu)造的行為。
將在以下兩個(gè)文件中顯示使用 PageModel 類的類似頁(yè)面。 Pages/Index2.cshtml 文件:
@page
@using RazorPagesIntro.Pages
@model IndexModel2
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Pages/Index2.cshtml.cs 頁(yè)面模型:
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
namespace RazorPagesIntro.Pages
{
public class IndexModel2 : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
按照慣例,PageModel 類文件的名稱與追加 .cs 的 Razor 頁(yè)面文件名稱相同。 例如,前面的 Razor 頁(yè)面的名稱為 Pages/Index2.cshtml。 包含 PageModel 類的文件的名稱為 Pages/Index2.cshtml.cs。
頁(yè)面的 URL 路徑的關(guān)聯(lián)由頁(yè)面在文件系統(tǒng)中的位置決定。 下表顯示了 Razor 頁(yè)面路徑及匹配的 URL:
| 文件名和路徑 | 匹配的 URL |
|---|---|
| /Pages/Index.cshtml |
/ 或 /Index
|
| /Pages/Contact.cshtml | /Contact |
| /Pages/Store/Contact.cshtml | /Store/Contact |
| /Pages/Store/Index.cshtml |
/Store 或 /Store/Index
|
注意:
默認(rèn)情況下,運(yùn)行時(shí)在“Pages”文件夾中查找 Razor 頁(yè)面文件。
URL 未包含頁(yè)面時(shí),Index為默認(rèn)頁(yè)面。
編寫基本窗體
Razor 頁(yè)面功能旨在簡(jiǎn)化 Web 瀏覽器常用的模式。 模型綁定、標(biāo)記幫助程序和 HTML 幫助程序均只可用于 Razor 頁(yè)面類中定義的屬性。 請(qǐng)參考為 Contact 模型實(shí)現(xiàn)基本的“聯(lián)系我們”窗體的頁(yè)面:
在本文檔中的示例中,DbContext 在 Startup.cs 文件中進(jìn)行初始化。
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();
}
}
}
數(shù)據(jù)模型:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Data
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
}
}
數(shù)據(jù)庫(kù)上下文:
using Microsoft.EntityFrameworkCore;
namespace RazorPagesContacts.Data
{
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet<Customer> Customers { 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 頁(yè)面模型:
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 類命名為 <PageName>Model 并且它與頁(yè)面位于同一個(gè)命名空間中。
使用 PageModel 類,可以將頁(yè)面的邏輯與其展示分離開來(lái)。 它定義了頁(yè)面處理程序,用于處理發(fā)送到頁(yè)面的請(qǐng)求和用于呈現(xiàn)頁(yè)面的數(shù)據(jù)。 借助這種分離,可以通過(guò)依賴關(guān)系注入管理頁(yè)面依賴關(guān)系,并對(duì)頁(yè)面執(zhí)行單元測(cè)試。
頁(yè)面包含 OnPostAsync 處理程序方法,它在 POST 請(qǐng)求上運(yùn)行(當(dāng)用戶發(fā)布窗體時(shí))。 可以為任何 HTTP 謂詞添加處理程序方法。 最常見的處理程序是:
-
OnGet,用于初始化頁(yè)面所需的狀態(tài)。 OnGet 示例。 -
OnPost,用于處理窗體提交。
Async 命名后綴為可選,但是按照慣例通常會(huì)將它用于異步函數(shù)。 前面示例中的 OnPostAsync 代碼看上去與通常在控制器中編寫的內(nèi)容相似。 前面的代碼通常用于 Razor 頁(yè)面。 多數(shù) MVC 基元(例如模型綁定、驗(yàn)證和操作結(jié)果)都是共享的。
之前的 OnPostAsync 方法:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
OnPostAsync 的基本流:
檢查驗(yàn)證錯(cuò)誤。
- 如果沒(méi)有錯(cuò)誤,則保存數(shù)據(jù)并重定向。
- 如果有錯(cuò)誤,則再次顯示頁(yè)面并附帶驗(yàn)證消息。 客戶端驗(yàn)證與傳統(tǒng)的 ASP.NET Core MVC 應(yīng)用程序相同。 很多情況下,都會(huì)在客戶端上檢測(cè)到驗(yàn)證錯(cuò)誤,并且從不將它們提交到服務(wù)器。
成功輸入數(shù)據(jù)后,OnPostAsync 處理程序方法調(diào)用 RedirectToPage 幫助程序方法來(lái)返回 RedirectToPageResult 的實(shí)例。 RedirectToPage 是新的操作結(jié)果,類似于 RedirectToAction 或 RedirectToRoute,但是已針對(duì)頁(yè)面進(jìn)行自定義。 在前面的示例中,它將重定向到根索引頁(yè) (/Index)。 頁(yè)面 URL 生成部分中詳細(xì)介紹了 RedirectToPage。
提交的窗體存在(已傳遞到服務(wù)器的)驗(yàn)證錯(cuò)誤時(shí),OnPostAsync 處理程序方法調(diào)用 Page 幫助程序方法。Page 返回 PageResult 的實(shí)例。 返回 Page 的過(guò)程與控制器中的操作返回 View 的過(guò)程相似。PageResult 是處理程序方法的默認(rèn) 返回類型。 返回 void 的處理程序方法將顯示頁(yè)面。
Customer 屬性使用 [BindProperty] 特性來(lái)選擇加入模型綁定。
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");
}
}
默認(rèn)情況下,Razor 頁(yè)面只綁定帶有非 GET 謂詞的屬性。 綁定屬性可以減少需要編寫的代碼量。 綁定通過(guò)使用相同的屬性顯示窗體字段 (<input asp-for="Customer.Name" />) 來(lái)減少代碼,并接受輸入。
備注
出于安全原因,必須選擇綁定 GET 請(qǐng)求數(shù)據(jù)以對(duì)模型屬性進(jìn)行分頁(yè)。 請(qǐng)?jiān)趯⒂脩糨斎胗成涞綄傩郧皩?duì)其進(jìn)行驗(yàn)證。 當(dāng)構(gòu)建依賴查詢字符串或路由值的功能時(shí),選擇加入此行為非常有用。
若要將屬性綁定在 GET 請(qǐng)求上,請(qǐng)將[BindProperty]特性的SupportsGet屬性設(shè)置為true:[BindProperty(SupportsGet = true)]
主頁(yè) (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 文件包含以下標(biāo)記來(lái)創(chuàng)建每個(gè)聯(lián)系人項(xiàng)的編輯鏈接:
<a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>
定位點(diǎn)標(biāo)記幫助程序 使用 asp-route-{value} 屬性生成“編輯”頁(yè)面的鏈接。 此鏈接包含路由數(shù)據(jù)及聯(lián)系人 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}" 告訴頁(yè)面接受包含 int 路由數(shù)據(jù)的頁(yè)面請(qǐng)求。 如果頁(yè)面請(qǐng)求未包含可轉(zhuǎn)換為 int 的路由數(shù)據(jù),則運(yùn)行時(shí)返回 HTTP 404(未找到)錯(cuò)誤。 若要使 ID 可選,請(qǐng)將 ? 追加到路由約束:
@page "{id:int?}"
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");
}
}
}
Index.cshtml 文件還包含用于為每個(gè)客戶聯(lián)系人創(chuàng)建刪除按鈕的標(biāo)記:
<button type="submit" asp-page-handler="delete"
asp-route-id="@contact.Id">delete</button>
刪除按鈕采用 HTML 呈現(xiàn),其 formaction 包括參數(shù):
asp-route-id 屬性指定的客戶聯(lián)系人 ID。
asp-page-handler 屬性指定的 handler 。
下面是呈現(xiàn)的刪除按鈕的示例,其中客戶聯(lián)系人 ID 為 1 :
<button type="submit" formaction="/?id=1&handler=delete">delete</button>
選中按鈕時(shí),向服務(wù)器發(fā)送窗體 POST 請(qǐng)求。 按照慣例,根據(jù)方案 OnPost[handler]Async 基于 handler 參數(shù)的值來(lái)選擇處理程序方法的名稱。
因?yàn)楸臼纠?handler 是 delete ,因此 OnPostDeleteAsync 處理程序方法用于處理 POST 請(qǐng)求。 如果 asp-page-handler 設(shè)置為不同值(如 remove ),則選擇名稱為 OnPostRemoveAsync 的頁(yè)面處理程序方法。
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();
}
OnPostDeleteAsync 方法:
接受來(lái)自查詢字符串的 id 。
使用 FindAsync 查詢客戶聯(lián)系人的數(shù)據(jù)庫(kù)。
如果找到客戶聯(lián)系人,則從客戶聯(lián)系人列表將其刪除。 數(shù)據(jù)庫(kù)將更新。
調(diào)用 RedirectToPage ,重定向到根索引頁(yè) (/Index) 。
XSRF/CSRF 和 Razor 頁(yè)面
無(wú)需為防偽驗(yàn)證編寫任何代碼。Razor 頁(yè)面自動(dòng)將防偽標(biāo)記生成過(guò)程和驗(yàn)證過(guò)程包含在內(nèi)。
將布局、分區(qū)、模板和標(biāo)記幫助程序用于 Razor 頁(yè)面
頁(yè)面可使用 Razor 視圖引擎的所有功能。 布局、分區(qū)、模板、標(biāo)記幫助程序、 _ViewStart.cshtml 和 _ViewImports.cshtml 的工作方式與它們?cè)趥鹘y(tǒng)的 Razor 視圖中的工作方式相同。
我們來(lái)使用其中的一些功能來(lái)整理此頁(yè)面。
向 Pages/_Layout.cshtml 添加布局頁(yè)面:
<!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>
布局:
- 控制每個(gè)頁(yè)面的布局(頁(yè)面選擇退出布局時(shí)除外)。
- 導(dǎo)入 HTML 結(jié)構(gòu),例如 JavaScript 和樣式表。
請(qǐng)參閱布局頁(yè)面了解詳細(xì)信息。
在 Pages/_ViewStart.cshtml 中設(shè)置 Layout 屬性:
@{
Layout = "_Layout";
}
注意:布局位于“頁(yè)面”文件夾中。 頁(yè)面按層次結(jié)構(gòu)從當(dāng)前頁(yè)面的文件夾開始查找其他視圖(布局、模板、分區(qū))。 可以從“頁(yè)面”文件夾下的任意 Razor 頁(yè)面使用“頁(yè)面”文件夾中的布局。
建議不要將布局文件放在“視圖/共享”文件夾中。 視圖/共享 是一種 MVC 視圖模式。Razor 頁(yè)面旨在依賴文件夾層次結(jié)構(gòu),而非路徑約定。
Razor 頁(yè)面中的視圖搜索包含“頁(yè)面”文件夾。 用于 MVC 控制器和傳統(tǒng) Razor 視圖的布局、模板和分區(qū)可直接工作。
添加 Pages/_ViewImports.cshtml 文件:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
本教程的后續(xù)部分中將介紹 @namespace 。 @addTagHelper 指令將內(nèi)置標(biāo)記幫助程序引入“頁(yè)面”文件夾中的所有頁(yè)面。
頁(yè)面上顯式使用 @namespace 指令后:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
此指令將為頁(yè)面設(shè)置命名空間。 @model 指令無(wú)需包含命名空間。
_ViewImports.cshtml 中包含 @namespace 指令后,指定的命名空間將為在導(dǎo)入 @namespace 指令的頁(yè)面中生成的命名空間提供前綴。 生成的命名空間的剩余部分(后綴部分)是包含 _ViewImports.cshtml 的文件夾與包含頁(yè)面的文件夾之間以點(diǎn)分隔的相對(duì)路徑。
例如,代碼隱藏文件 Pages/Customers/Edit.cshtml.cs 顯式設(shè)置命名空間:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
Pages/_ViewImports.cshtml 文件設(shè)置以下命名空間:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
為 Pages/Customers/Edit.cshtml Razor 頁(yè)面生成的命名空間與代碼隱藏文件相同. 已對(duì) @namespace 指令進(jìn)行設(shè)計(jì),因此添加到項(xiàng)目的 C# 類和頁(yè)面生成的代碼可直接工作,而無(wú)需添加代碼隱藏文件的 @using 指令。
注意: @namespace 也可用于傳統(tǒng)的 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>
更新后的 Pages/Create.cshtml 視圖文件:
@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>
頁(yè)面的 URL 生成
之前顯示的 Create 頁(yè)面使用 RedirectToPage :
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
應(yīng)用具有以下文件/文件夾結(jié)構(gòu):
- /Pages
- Index.cshtml
- /Customer
- Create.cshtml
- Edit.cshtml
- Index.cshtml
成功后, Pages/Customers/Create.cshtml 和 Pages/Customers/Edit.cshtml 頁(yè)面將重定向到 Pages/Index.cshtml 。字符串 /Index 是用于訪問(wèn)上一頁(yè)的 URI 的組成部分??梢允褂米址?/Index 生成 Pages/Index.cshtml 頁(yè)面的 URI 。例如:
Url.Page("/Index", ...)<a asp-page="/Index">My Index Page</a>RedirectToPage("/Index")
頁(yè)面名稱是從根“/Pages”文件夾到頁(yè)面的路徑(包含前導(dǎo) / ,例如 /Index )。相較于僅對(duì) URL 硬編碼,前面的 URL 生成示例的功能更加強(qiáng)大。URL 生成使用路由,并且可以根據(jù)目標(biāo)路徑定義路由的方式生成參數(shù)并對(duì)參數(shù)編碼。
頁(yè)面的URL生成支持相對(duì)名稱。 下表顯示了 Pages/Customers/Create.cshtml 中不同的 RedirectToPage 參數(shù)選擇的索引頁(yè):
| RedirectToPage(x) | 頁(yè) |
|---|---|
| RedirectToPage("/Index") | Pages/Index |
| RedirectToPage("./Index") | Pages/Customers/Index |
| RedirectToPage("../Index") | Pages/Index |
| RedirectToPage("Index") | Pages/Customers/Index |
RedirectToPage("Index") 、 RedirectToPage("./Index") 和 RedirectToPage("../Index") 是相對(duì)名稱。 結(jié)合 RedirectToPage 參數(shù)與當(dāng)前頁(yè)的路徑來(lái)計(jì)算目標(biāo)頁(yè)面的名稱。
構(gòu)建結(jié)構(gòu)復(fù)雜的站點(diǎn)時(shí),相對(duì)名稱鏈接很有用。 如果使用相對(duì)名稱鏈接文件夾中的頁(yè)面,則可以重命名該文件夾。 所有鏈接仍然有效(因?yàn)檫@些鏈接未包含此文件夾名稱)。
TempData
ASP.NET在控制器上公開了 TempData 屬性。 此屬性存儲(chǔ)未讀取的數(shù)據(jù)。 Keep 和 Peek 方法可用于檢查數(shù)據(jù),而不執(zhí)行刪除。 多個(gè)請(qǐng)求需要數(shù)據(jù)時(shí), TempData 有助于進(jìn)行重定向。
[TempData] 是 ASP.NET Core 2.0 中的新屬性,在控制器和頁(yè)面上受支持。
下面的代碼使用 TempData 設(shè)置 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 文件中的以下標(biāo)記使用 TempData 顯示 Message 的值。
<h3>Msg: @Model.Message</h3>
Pages/Customers/Index.cshtml.cs頁(yè)面模型將[TempData]屬性應(yīng)用到Message屬性。
[TempData]
public string Message { get; set; }
請(qǐng)參閱 TempData 了解詳細(xì)信息。
針對(duì)一個(gè)頁(yè)面的多個(gè)處理程序
以下頁(yè)面使用 asp-page-handler 標(biāo)記幫助程序?yàn)閮蓚€(gè)頁(yè)面處理程序生成標(biāo)記:
@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>
前面示例中的窗體包含兩個(gè)提交按鈕,每個(gè)提交按鈕均使用 FormActionTagHelper 提交到不同的 URL 。 asp-page-handler 是 asp-page 的配套屬性。 asp-page-handler 生成提交到頁(yè)面定義的各個(gè)處理程序方法的 URL 。未指定 asp-page ,因?yàn)槭纠焰溄拥疆?dāng)前頁(yè)面。
頁(yè)面模型:
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();
}
}
}
前面的代碼使用已命名處理程序方法。已命名處理程序方法通過(guò)采用名稱中 On<HTTP Verb> 之后及 Async 之前的文本(如果有)創(chuàng)建。 在前面的示例中,頁(yè)面方法是 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" />
使用前面的代碼時(shí),提交到 OnPostJoinListAsync 的 URL 路徑為 http://localhost:5000/Customers/CreateFATH?handler=JoinList 。提交到 OnPostJoinListUCAsync 的 URL 路徑為 http://localhost:5000/Customers/CreateFATH?handler=JoinListUC 。
自定義路由
如果你不喜歡 URL 中的查詢字符串 ?handler=JoinList,可以更改路由,將處理程序名稱放在 URL 的路徑部分??梢酝ㄟ^(guò)在 @page 指令后面添加使用雙引號(hào)括起來(lái)的路由模板來(lái)自定義路由。
@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 前面的 ? 表示路由參數(shù)為可選。
可以使用 @page 將其他段和參數(shù)添加到頁(yè)面的路由中。 其中的任何內(nèi)容均會(huì)被追加到頁(yè)面的默認(rèn)路由中。 不支持使用絕對(duì)路徑或虛擬路徑更改頁(yè)面的路由(如 "~/Some/Other/Path" )。
配置和設(shè)置
若要配置高級(jí)選項(xiàng),請(qǐng)?jiān)?MVC 生成器上使用 AddRazorPagesOptions 擴(kuò)展方法:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
}
目前,可以使用 RazorPagesOptions 設(shè)置頁(yè)面的根目錄,或者為頁(yè)面添加應(yīng)用程序模型約定。 通過(guò)這種方式,我們?cè)趯?lái)會(huì)實(shí)現(xiàn)更多擴(kuò)展功能。
若要預(yù)編譯視圖,請(qǐng)參閱 Razor 視圖編譯。
請(qǐng)參閱 Razor 頁(yè)面入門,這篇文章以本文為基礎(chǔ)編寫。
指定 Razor 頁(yè)面位于內(nèi)容根目錄中
默認(rèn)情況下,Razor 頁(yè)面位于 /Pages 目錄的根位置。向 AddMvc 添加 WithRazorPagesAtContentRoot,以指定 Razor 頁(yè)面位于應(yīng)用的內(nèi)容根目錄 (ContentRootPath) 中:
services.AddMvc()
.AddRazorPagesOptions(options =>
{
...
})
.WithRazorPagesAtContentRoot();
指定 Razor 頁(yè)面位于自定義根目錄中
向 AddMvc 添加 WithRazorPagesRoot,以指定 Razor 頁(yè)面位于應(yīng)用中自定義根目錄位置(提供相對(duì)路徑):
services.AddMvc()
.AddRazorPagesOptions(options =>
{
...
})
.WithRazorPagesRoot("/path/to/razor/pages");