系统必备
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
创建 Razor 页面项目
Razor 页面
Startup.cs 中已启用 Razor 页面:
C#
复制
public
class
Startup
{
public
void
ConfigureServices
(IServiceCollection services)
{
// Includes support for Razor Pages and controllers.
services.AddMvc();
}
public
void
Configure
(IApplicationBuilder app)
{
app.UseMvc();
}
}
请考虑一个基本页面:
CSHTML
复制
@page
<
h1
>
Hello, world!
</
h1
>
<
h2
>
The time on the server is @DateTime.Now
</
h2
>
上述代码看上去类似于一个 Razor 视图文件。
不同之处在于
@page
指令。
@page
使文件转换为一个 MVC 操作 ,这意味着它将直接处理请求,而无需通过控制器处理。
@page
必须是页面上的第一个 Razor 指令。
@page
将影响其他 Razor 构造的行为。
将在以下两个文件中显示使用
PageModel
类的类似页面。
Pages/Index2.cshtml 文件:
CSHTML
复制
@page
@using RazorPagesIntro.Pages
@model IndexModel2
<
h2
>
Separate page model
</
h2
>
<
p
>
@Model.Message
</
p
>
Pages/Index2.cshtml.cs 页面模型:
C#
复制
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 页面文件名称相同。
例如,前面的 Razor 页面的名称为 Pages/Index2.cshtml。
包含
PageModel
类的文件的名称为 Pages/Index2.cshtml.cs。
页面的 URL 路径的关联由页面在文件系统中的位置决定。 下表显示了 Razor 页面路径及匹配的 URL:
文件名和路径 | 匹配的 URL |
/Pages/Index.cshtml | / 或 /Index |
/Pages/Contact.cshtml | /Contact |
/Pages/Store/Contact.cshtml | /Store/Contact |
/Pages/Store/Index.cshtml | /Store 或 /Store/Index |
注意:
- 默认情况下,运行时在“Pages”文件夹中查找 Razor 页面文件。
- URL 未包含页面时,Index 为默认页面。
编写基本窗体
由于 Razor 页面的设计,在构建应用时可轻松实施用于 Web 浏览器的常用模式。
模型绑定
、
标记帮助程序
和 HTML 帮助程序均只可用于 Razor 页面类中定义的属性。
请参考为
Contact
模型实现基本的“联系我们”窗体的页面:
C#
复制
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();
}
}
}
数据模型:
C#
复制
namespace
RazorPagesContacts.Data
{
public
class
Customer
{
public
int
Id {
get
;
set
; }
[
Required, StringLength(100)
]
public
string
Name {
get
;
set
; }
}
}
数据库上下文:
C#
复制
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 视图文件:
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"
>
<
input
type
=
"submit"
/>
</
form
>
</
body
>
</
html
>
Pages/Create.cshtml.cs 页面模型:
C#
复制
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
并且它与页面位于同一个命名空间中。
使用
PageModel
类,可以将页面的逻辑与其展示分离开来。
它定义了页面处理程序,用于处理发送到页面的请求和用于呈现页面的数据。
借助这种分离,可以通过
依赖关系注入
管理页面依赖关系,并对页面执行
单元测试
。
页面包含
OnPostAsync
处理程序方法,它在
POST
请求上运行(当用户发布窗体时)。
可以为任何 HTTP 谓词添加处理程序方法。 最常见的处理程序是:
- OnGet,用于初始化页面所需的状态。 OnGet 示例。
- OnPost,用于处理窗体提交。
Async
命名后缀为可选,但是按照惯例通常会将它用于异步函数。
前面示例中的
OnPostAsync
代码看上去与通常在控制器中编写的内容相似。
前面的代码通常用于 Razor 页面。
多数 MVC 基元(例如
模型绑定
、
验证
和操作结果)都是共享的。
之前的
OnPostAsync
方法:
C#
复制
public
async
Task<IActionResult>
OnPostAsync
()
{
if
(!ModelState.IsValid)
{
return
Page();
}
_db.Customers.Add(Customer);
await
_db.SaveChangesAsync();
return
RedirectToPage(
"/Index"
);
}
OnPostAsync
的基本流:
检查验证错误。
- 如果没有错误,则保存数据并重定向。
- 如果有错误,则再次显示页面并附带验证消息。 客户端验证与传统的 ASP.NET Core MVC 应用程序相同。 很多情况下,都会在客户端上检测到验证错误,并且从不将它们提交到服务器。
成功输入数据后,
OnPostAsync
处理程序方法调用
RedirectToPage
帮助程序方法来返回
RedirectToPageResult
的实例。
RedirectToPage
是新的操作结果,类似于
RedirectToAction
或
RedirectToRoute
,但是已针对页面进行自定义。
在前面的示例中,它将重定向到根索引页 (
/Index
)。
页面 URL 生成
部分中详细介绍了
RedirectToPage
。
提交的窗体存在(已传递到服务器的)验证错误时,
OnPostAsync
处理程序方法调用
Page
帮助程序方法。
Page
返回
PageResult
的实例。
返回
Page
的过程与控制器中的操作返回
View
的过程相似。
PageResult
是处理程序方法的默认 返回类型。
返回
void
的处理程序方法将显示页面。
Customer
属性使用
[BindProperty]
特性来选择加入模型绑定。
C#
复制
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 页面只绑定带有非 GET 谓词的属性。 绑定属性可以减少需要编写的代码量。
绑定通过使用相同的属性显示窗体字段 (
<input asp-for="
Customer.Name
" />
) 来减少代码,并接受输入。
备注
出于安全原因,必须选择绑定 GET 请求数据以对模型属性进行分页。 请在将用户输入映射到属性前对其进行验证。 当处理依赖查询字符串或路由值的方案时,选择加入此行为非常有用。
若要将属性绑定在 GET 请求上,请将
[BindProperty]
特性的
SupportsGet
属性设置为
true
:
[BindProperty(SupportsGet = true)]
主页 (Index.cshtml):
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
>
<
button
type
=
"submit"
asp-page-handler
=
"delete"
</
td
>
</
tr
>
}
</
tbody
>
</
table
>
<
a
asp-page
=
"./Create"
>
Create
</
a
>
</
form
>
Index.cshtml.cs 隐藏文件:
C#
复制
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 文件包含以下标记来创建每个联系人项的编辑链接:
CSHTML
复制
Pages/Edit.cshtml 文件:
CSHTML
复制
@page "{id:int}"
@model RazorPagesContacts.Pages.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
ViewData["Title"] = "Edit Customer";
}
<
form
method
=
"post"
>
<
div
asp-validation-summary
=
"All"
>
</
div
>
<
div
>
<
div
>
</
div
>
</
div
>
<
div
>
<
button
type
=
"submit"
>
Save
</
button
>
</
div
>
</
form
>
第一行包含
@page "{id:int}"
指令。
路由约束
"{id:int}"
告诉页面接受包含
int
路由数据的页面请求。
如果页面请求未包含可转换为
int
的路由数据,则运行时返回 HTTP 404(未找到)错误。
若要使 ID 可选,请将
?
追加到路由约束:
CSHTML
复制
@page "{id:int?}"
Pages/Edit.cshtml.cs 文件:
C#
复制
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)
{
}
return
RedirectToPage(
"/Index"
);
}
}
}
Index.cshtml 文件还包含用于为每个客户联系人创建删除按钮的标记:
CSHTML
复制
<
button
type
=
"submit"
asp-page-handler
=
"delete"
删除按钮采用 HTML 呈现,其
formaction
包括参数:
- asp-route-id 属性指定的客户联系人 ID。
- asp-page-handler 属性指定的 handler。
下面是呈现的删除按钮的示例,其中客户联系人 ID 为
1
:
HTML
复制
<
button
type
=
"submit"
formaction
=
"/?id=1&handler=delete"
>
delete
</
button
>
选中按钮时,向服务器发送窗体
POST
请求。
按照惯例,根据方案
OnPost[handler]Async
基于
handler
参数的值来选择处理程序方法的名称。
因为本示例中
handler
是
delete
,因此
OnPostDeleteAsync
处理程序方法用于处理
POST
请求。
如果
asp-page-handler
设置为不同值(如
remove
),则选择名称为
OnPostRemoveAsync
的页面处理程序方法。
C#
复制
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
方法:
- 接受来自查询字符串的 id。
- 使用 FindAsync 查询客户联系人的数据库。
- 如果找到客户联系人,则从客户联系人列表将其删除。 数据库将更新。
- 调用 RedirectToPage,重定向到根索引页 (/Index)。
标记所需的页属性
[!code-cs
]
使用 OnGet 处理程序管理 HEAD 请求
通常,针对 HEAD 请求创建和调用 HEAD 处理程序:
C#
复制
public
void
OnHead
()
{
HttpContext.Response.Headers.Add(
"HandledBy"
,
"Handled by OnHead!"
);
}
如果未定义 HEAD 处理程序 (
OnHead
),Razor 页面会回退以调用
ASP.NET
Core 2.1 或更高版本中的 GET 页处理程序 (
OnGet
)。
使用
ASP.NET
Core 2.1 到 2.x 版本
Startup.Configure
中的
SetCompatibilityVersion 方法
,选择加入此行为:
C#
复制
services.AddMvc()
.SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
SetCompatibilityVersion
有效地将 Razor 页面选项
AllowMappingHeadRequestsToGetHandler
设置为
true
。
可以显式地选择使用特定行为,而不是通过
SetCompatibilityVersion
选择使用所有 2.1 行为。
以下代码选择使用将 HEAD 映射到 GET 处理程序这一行为。
C#
复制
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.AllowMappingHeadRequestsToGetHandler =
true
;
});
XSRF/CSRF 和 Razor 页面
将布局、分区、模板和标记帮助程序用于 Razor 页面
页面可使用 Razor 视图引擎的所有功能。 布局、分区、模板、标记帮助程序、_ViewStart.cshtml 和 _ViewImports.cshtml 的工作方式与它们在传统的 Razor 视图中的工作方式相同。
让我们使用其中的一些功能来整理此页面。
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
>
布局
:
- 控制每个页面的布局(页面选择退出布局时除外)。
- 导入 HTML 结构,例如 JavaScript 和样式表。
CSHTML
复制
@{
Layout = "_Layout";
}
布局位于“页面”文件夹中。 页面按层次结构从当前页面的文件夹开始查找其他视图(布局、模板、分区)。 可以从“页面”文件夹下的任意 Razor 页面使用“页面”文件夹中的布局。
建议不要将布局文件放在“视图/共享”文件夹中。 视图/共享 是一种 MVC 视图模式。 Razor 页面旨在依赖文件夹层次结构,而非路径约定。
Razor 页面中的视图搜索包含“页面”文件夹。 用于 MVC 控制器和传统 Razor 视图的布局、模板和分区可直接工作。
添加 Pages/_ViewImports.cshtml 文件:
CSHTML
复制
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
页面上显式使用
@namespace
指令后:
CSHTML
复制
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<
h2
>
Name space
</
h2
>
<
p
>
@Model.Message
</
p
>
此指令将为页面设置命名空间。
@model
指令无需包含命名空间。
_ViewImports.cshtml 中包含
@namespace
指令后,指定的命名空间将为在导入
@namespace
指令的页面中生成的命名空间提供前缀。
生成的命名空间的剩余部分(后缀部分)是包含 _ViewImports.cshtml 的文件夹与包含页面的文件夹之间以点分隔的相对路径。
例如,代码隐藏文件 Pages/Customers/Edit.cshtml.cs 显式设置命名空间:
C#
复制
namespace
RazorPagesContacts.Pages
{
public
class
EditModel
:
PageModel
{
private
readonly
AppDbContext _db;
public
EditModel
(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
Pages/_ViewImports.cshtml 文件设置以下命名空间:
CSHTML
复制
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
为 Pages/Customers/Edit.cshtml Razor 页面生成的命名空间与代码隐藏文件相同.
已对
@namespace
指令进行设计,因此添加到项目的 C# 类和页面生成的代码可直接工作,而无需添加代码隐藏文件的
@using
指令。
@namespace
也可用于传统的 Razor 视图。
原始的 Pages/Create.cshtml 视图文件:
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"
>
<
input
type
=
"submit"
/>
</
form
>
</
body
>
</
html
>
更新后的 Pages/Create.cshtml 视图文件:
CSHTML
复制
@page
@model CreateModel
<
html
>
<
body
>
<
p
>
Enter your name.
</
p
>
<
div
asp-validation-summary
=
"All"
>
</
div
>
<
form
method
=
"POST"
>
<
input
type
=
"submit"
/>
</
form
>
</
body
>
</
html
>
Razor 页面初学者项目
包含 Pages/_ValidationScriptsPartial.cshtml,它与客户端验证联合。
页面的 URL 生成
之前显示的
Create
页面使用
RedirectToPage
:
C#
复制
public
async
Task<IActionResult>
OnPostAsync
()
{
if
(!ModelState.IsValid)
{
return
Page();
}
_db.Customers.Add(Customer);
await
_db.SaveChangesAsync();
return
RedirectToPage(
"/Index"
);
}
应用具有以下文件/文件夹结构:
-
/Pages
-
Index.cshtml
-
/Customers
- Create.cshtml
- Edit.cshtml
- Index.cshtml
-
成功后,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
参数选择的索引页:
RedirectToPage(x) | 页 |
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
参数与当前页的路径来计算目标页面的名称。
构建结构复杂的站点时,相对名称链接很有用。 如果使用相对名称链接文件夹中的页面,则可以重命名该文件夹。 所有链接仍然有效(因为这些链接未包含此文件夹名称)。
ViewData 特性
可以通过
ViewDataAttribute
将数据传递到页面。
控制器或 Razor 页面模型上使用
[ViewData]
修饰的属性将其值存储在
ViewDataDictionary
中并从此处进行加载。
在下面的示例中,
AboutModel
包含使用
[ViewData]
修饰的
Title
属性。
Title
属性设置为“关于”页面的标题:
C#
复制
public
class
AboutModel
:
PageModel
{
[
ViewData
]
public
string
Title {
get
; } =
"About"
;
public
void
OnGet
()
{
}
}
在“关于”页面中,以模型属性的形式访问
Title
属性:
CSHTML
复制
<
h1
>
@Model.Title
</
h1
>
在布局中,从 ViewData 字典读取标题:
CSHTML
复制
<!DOCTYPE html>
<
html
lang
=
"en"
>
<
head
>
<
title
>
@ViewData["Title"] - WebApplication
</
title
>
...
TempData
ASP.NET
在
控制器
上公开了
TempData
属性。
此属性存储未读取的数据。
Keep
和
Peek
方法可用于检查数据,而不执行删除。
多个请求需要数据时,
TempData
有助于进行重定向。
下面的代码使用
TempData
设置
Message
的值:
C#
复制
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();
return
RedirectToPage(
"./Index"
);
}
}
Pages/Customers/Index.cshtml 文件中的以下标记使用
TempData
显示
Message
的值。
CSHTML
复制
<
h3
>
Msg: @Model.Message
</
h3
>
Pages/Customers/Index.cshtml.cs 页面模型将
[TempData]
属性应用到
Message
属性。
C#
复制
[
TempData
]
public
string
Message {
get
;
set
; }
针对一个页面的多个处理程序
以下页面使用
asp-page-handler
标记帮助程序为两个页面处理程序生成标记:
CSHTML
复制
@page
@model CreateFATHModel
<
html
>
<
body
>
<
p
>
Enter your name.
</
p
>
<
div
asp-validation-summary
=
"All"
>
</
div
>
<
form
method
=
"POST"
>
<
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
,因为示例已链接到当前页面。
页面模型:
C#
复制
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();
}
return
await
OnPostJoinListAsync();
}
}
}
前面的代码使用已命名处理程序方法。
已命名处理程序方法通过采用名称中
On<HTTP Verb>
之后及
Async
之前的文本(如果有)创建。
在前面的示例中,页面方法是 OnPost
JoinList
Async 和 OnPost
JoinListUC
Async。
删除 OnPost 和 Async 后,处理程序名称为
JoinList
和
JoinListUC
。
CSHTML
复制
<
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
指令后面添加使用双引号括起来的路由模板来自定义路由。
CSHTML
复制
@page "{handler?}"
@model CreateRouteModel
<
html
>
<
body
>
<
p
>
Enter your name.
</
p
>
<
div
asp-validation-summary
=
"All"
>
</
div
>
<
form
method
=
"POST"
>
<
input
type
=
"submit"
asp-page-handler
=
"JoinList"
value
=
"Join"
/>
<
input
type
=
"submit"
asp-page-handler
=
"JoinListUC"
value
=
"JOIN UC"
/>
</
form
>
</
body
>
</
html
>
前面的路由将处理程序放在了 URL 路径中,而不是查询字符串中。
handler
前面的
?
表示路由参数为可选。
可以使用
@page
将其他段和参数添加到页面的路由中。
其中的任何内容均会被追加到页面的默认路由中。
不支持使用绝对路径或虚拟路径更改页面的路由(如
"~/Some/Other/Path"
)。
配置和设置
若要配置高级选项,请在 MVC 生成器上使用
AddRazorPagesOptions
扩展方法:
C#
复制
public
void
ConfigureServices
(IServiceCollection services)
{
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.RootDirectory =
"/MyPages"
;
options.Conventions.AuthorizeFolder(
"/MyPages/Admin"
);
});
}
目前,可以使用
RazorPagesOptions
设置页面的根目录,或者为页面添加应用程序模型约定。
通过这种方式,我们在将来会实现更多扩展功能。
指定 Razor 页面位于内容根目录中
默认情况下,Razor 页面位于 /Pages 目录的根位置。
向
AddMvc
添加
WithRazorPagesAtContentRoot
,以指定 Razor 页面位于应用的内容根目录 (
ContentRootPath
) 中:
C#
复制
services.AddMvc()
.AddRazorPagesOptions(options =>
{
...
})
.WithRazorPagesAtContentRoot();
指定 Razor 页面位于自定义根目录中
C#
复制
services.AddMvc()
.AddRazorPagesOptions(options =>
{
...
})
.WithRazorPagesRoot(
"/path/to/razor/pages"
);