Visual Studio Code 说明使用用于 ASP.NET Core 的 .NET CLI 开发功能,如项目创建。 可在(macOS、Linux 或 Windows)上或在任何代码编辑器中遵循这些说明。 如果使用 Visual Studio Code 以外的其他内容,则可能需要进行少量更改。
在命令行中运行 dotnet new webapp
。
Razor 中已启用 Program.cs
Pages:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
在上述代码中:
请考虑一个基本页面:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
前面的代码与具有控制器和视图的 ASP.NET Core 应用中使用的 Razor 视图文件 非常相似。 不同之处在于 @page
指令。 @page
将文件转换为 MVC 操作,这意味着它可以直接处理请求,而无需经过控制器。 @page
必须是页面上的第一个 Razor 指令。 @page
会影响其他 Razor 构造的行为。 Razor Pages 文件名有 .cshtml
后缀。
将在以下两个文件中显示使用 PageModel
类的类似页面。 Pages/Index2.cshtml
文件:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Pages/Index2.cshtml.cs
页面模型:
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
按照惯例,PageModel
类文件的名称与追加 Razor 的 .cs
Page 文件名称相同。 例如,前面的 Razor Page 的名称为 Pages/Index2.cshtml
。 包含 PageModel
类的文件的名称为 Pages/Index2.cshtml.cs
。
页面的 URL 路径的关联由页面在文件系统中的位置决定。 下表显示了 Razor Page 路径及匹配的 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 Pages 文件。
URL 未包含页面时,Index
为默认页面。
由于 Razor Pages 的设计,在构建应用时可轻松实施用于 Web 浏览器的常用模式。 模型绑定 、标记帮助程序 和 HTML 帮助程序可使用 Razor Page 类中定义的属性。 请参考为 Contact
模型实现的基本的“联系我们”窗体页面:
在本文档中的示例中,DbContext
在 Program.cs 文件中进行初始化。
内存中数据库需要 Microsoft.EntityFrameworkCore.InMemory
NuGet 包。
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
数据模型:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
数据库上下文:
using Microsoft.EntityFrameworkCore;
namespace RazorPagesContacts.Data
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext (DbContextOptions<CustomerDbContext> options)
: base(options)
{
}
public DbSet<RazorPagesContacts.Models.Customer> Customer => Set<RazorPagesContacts.Models.Customer>();
}
}
Pages/Customers/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
Pages/Customers/Create.cshtml.cs
页面模型:
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
按照惯例,PageModel
类命名为 <PageName>Model
并且它与页面位于同一个命名空间中。
使用 PageModel
类,可以将页面的逻辑与其展示分离开来。 它定义了页面处理程序,用于处理发送到页面的请求和用于呈现页面的数据。 这种隔离可实现:
页面包含 OnPostAsync
处理程序方法 ,它在 POST
请求上运行(当用户发布窗体时)。 可以添加任何 HTTP 谓词的处理程序方法。 最常见的处理程序是:
OnGet
,用于初始化页面所需的状态。 在上面的代码中,OnGet
方法显示 Create.cshtml
Razor Page。
OnPost
,用于处理窗体提交。
Async
命名后缀为可选,但是按照惯例通常会将它用于异步函数。 前面的代码通常用于 Razor Pages。
如果你熟悉使用控制器和视图的 ASP.NET 应用:
前面示例中的 OnPostAsync
代码类似于典型的控制器代码。
大多数 MVC 基元(例如模型绑定 、验证 和操作结果)通过控制器和通过 Razor Pages 的运作方式相同。
之前的 OnPostAsync
方法:
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
OnPostAsync
的基本流:
检查验证错误。
如果没有错误,则保存数据并重定向。
如果有错误,则再次显示页面并附带验证消息。 很多情况下,都会在客户端上检测到验证错误,并且从不将它们提交到服务器。
Pages/Customers/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
Pages/Customers/Create.cshtml
中呈现的 HTML:
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
在前面的代码中,发布窗体:
Customer
属性使用 [BindProperty]
特性来选择加入模型绑定:
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
[BindProperty]
不应 用于包含不应由客户端更改的属性的模型。 有关详细信息,请参阅过度发布 。
Razor Pages 只绑定带有非 GET
谓词的属性。 如果绑定到属性,则无需通过编写代码将 HTTP 数据转换为模型类型。 绑定通过使用相同的属性显示窗体字段 (<input asp-for="Customer.Name">
) 来减少代码,并接受输入。
查看 Pages/Customers/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
在前面的代码中,输入标记帮助程序 <input asp-for="Customer.Name" />
将 HTML <input>
元素绑定到 Customer.Name
模型表达式。
使用 @addTagHelper
提供标记帮助程序。
Index.cshtml
是主页:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@if (Model.Customers != null)
{
foreach (var contact in Model.Customers)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
关联的 PageModel
类 (Index.cshtml.cs
):
public class IndexModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public IndexModel(Data.CustomerDbContext context)
{
_context = context;
}
public IList<Customer>? Customers { get; set; }
public async Task OnGetAsync()
{
Customers = await _context.Customer.ToListAsync();
}
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
}
Index.cshtml
文件包含以下标记:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<a /a>
定位点标记帮助程序 使用 asp-route-{value}
属性生成“编辑”页面的链接。 此链接包含路由数据及联系人 ID。 例如 https://localhost:5001/Edit/1
。 标记帮助程序 使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素。
Index.cshtml
文件包含用于为每个客户联系人创建删除按钮的标记:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
呈现的 HTML:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
删除按钮采用 HTML 呈现,其 formaction 包括参数:
asp-route-id
属性指定的客户联系人 ID。
handler
属性指定的 asp-page-handler
。
选中按钮时,向服务器发送窗体 POST
请求。 按照惯例,根据方案 handler
基于 OnPost[handler]Async
参数的值来选择处理程序方法的名称。
因为本示例中 handler
是 delete
,因此 OnPostDeleteAsync
处理程序方法用于处理 POST
请求。 如果 asp-page-handler
设置为其他值(如 remove
),则选择名称为 OnPostRemoveAsync
的处理程序方法。
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
OnPostDeleteAsync
方法:
获取来自查询字符串的 id
。
使用 FindAsync
查询客户联系人的数据库。
如果找到客户联系人,则会将其删除,并更新数据库。
调用 RedirectToPage ,重定向到根索引页 (/Index
)。
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Customer</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Customer!.Id" />
<div class="form-group">
<label asp-for="Customer!.Name" class="control-label"></label>
<input asp-for="Customer!.Name" class="form-control" />
<span asp-validation-for="Customer!.Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
第一行包含 @page "{id:int}"
指令。 路由约束 "{id:int}"
告诉页面接受包含 int
路由数据的页面请求。 如果页面请求未包含可转换为 int
的路由数据,则运行时返回 HTTP 404(未找到)错误。 若要使 ID 可选,请将 ?
追加到路由约束:
@page "{id:int?}"
Edit.cshtml.cs
文件:
public class EditModel : PageModel
{
private readonly RazorPagesContacts.Data.CustomerDbContext _context;
public EditModel(RazorPagesContacts.Data.CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Customer = await _context.Customer.FirstOrDefaultAsync(m => m.Id == id);
if (Customer == null)
{
return NotFound();
}
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null)
{
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(Customer.Id))
{
return NotFound();
}
else
{
throw;
}
}
}
return RedirectToPage("./Index");
}
private bool CustomerExists(int id)
{
return _context.Customer.Any(e => e.Id == id);
}
}
验证规则:
在模型类中以声明方式指定。
在应用中的所有位置强制执行。
System.ComponentModel.DataAnnotations 命名空间提供一组内置验证特性,可通过声明方式应用于类或属性。 DataAnnotations 还包含 [DataType]
等格式特性,有助于格式设置但不提供任何验证。
请考虑 Customer
模型:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
使用以下 Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
前面的代码:
包括 jQuery 和 jQuery 验证脚本。
使用 <div />
和 <span />
标记帮助程序 以实现:
则会生成以下 HTML:
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
<script src="/lib/jquery/dist/jquery.js"></script>
<script src="/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
如果在不使用名称值的情况下发布“创建”窗体,则将在窗体上显示错误消息“名称字段是必需的”。 如果客户端上已启用 JavaScript,浏览器会显示错误,而不会发布到服务器。
[StringLength(10)]
特性在呈现的 HTML 上生成 data-val-length-max="10"
。 data-val-length-max
阻止浏览器输入超过指定最大长度的内容。 如果使用 Fiddler 等工具来编辑和重播文章:
对于长度超过 10 的名称。
返回错误消息“‘名称’字段必须是最大长度为 10 的字符串。”
考虑下列 Movie
模型:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
验证特性指定要对应用这些特性的模型属性强制执行的行为:
Required
和 MinimumLength
特性表示属性必须有值,但用户可输入空格来满足此验证。
RegularExpression
特性用于限制可输入的字符。 在上述代码中,即“Genre”(分类):
只能使用字母。
第一个字母必须为大写。 不允许使用空格、数字和特殊字符。
RegularExpression
“Rating”(分级):
要求第一个字符为大写字母。
允许在后续空格中使用特殊字符和数字。 “PG-13”对“分级”有效,但对于“分类”无效。
Range
特性将值限制在指定范围内。
StringLength
特性可以设置字符串属性的最大长度,以及可选的最小长度。
从本质上来说,需要值类型(如 decimal
、int
、float
、DateTime
),但不需要 [Required]
特性。
Movie
模型的“创建”页面显示无效值错误:
有关详细信息,请参阅:
将 CSS 样式隔离到各个页面、视图和组件以减少或避免:
若要为页面或视图添加限定范围的 CSS 文件,请将 CSS 样式置于与 文件的名称匹配的配套 .cshtml.css
文件中。 在下面的示例中,Index.cshtml.css
文件提供只应用于 Index.cshtml
页面或视图的 CSS 样式。
Pages/Index.cshtml.css
(Razor Pages) 或 Views/Index.cshtml.css
(MVC):
h1 {
color: red;
}
CSS 隔离在生成时发生。 框架会重写 CSS 选择器,以匹配应用页面或视图所呈现的标记。 重写的 CSS 样式作为静态资产 {APP ASSEMBLY}.styles.css
进行捆绑和生成。 占位符 {APP ASSEMBLY}
是项目的程序集名称。 指向捆绑 CSS 样式的链接放置在应用的布局中。
在应用 <head>
(Pages/Shared/_Layout.cshtml
Pages) 或 Razor (MVC) 的 Views/Shared/_Layout.cshtml
内容中,添加或确认是否存在指向捆绑 CSS 样式的链接:
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />
在下面的示例中,应用的程序集名称为 WebApp
:
<link rel="stylesheet" href="WebApp.styles.css" />
在限定范围的 CSS 文件中定义的样式仅应用于匹配文件的呈现输出。 在上面的示例中,在应用的其他位置定义的任何 h1
CSS 声明都不会与 Index
标头样式冲突。 CSS 样式级联和继承规则对限定范围的 CSS 文件仍然有效。 例如,直接应用于 <h1>
文件中的 Index.cshtml
元素的样式会替代 Index.cshtml.css
中限定范围的 CSS 文件的样式。
备注
为了保证发生捆绑时的 CSS 样式隔离,不支持在 Razor 代码块中导入 CSS。
CSS 隔离仅适用于 HTML 元素。 标记帮助程序 不支持 CSS 隔离。
在捆绑 CSS 文件中,每个页面、视图或 Razor 组件都与格式为 b-{STRING}
的范围标识符相关联,其中 {STRING}
占位符是框架生成的十个字符的字符串。 下面的示例提供了 <h1>
Pages 应用 Index
页面中前面 Razor 元素的样式:
/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
color: red;
}
在从捆绑文件应用 CSS 样式的 Index
页面中,范围标识符追加为 HTML 属性:
<h1 b-3xxtam6d07>
标识符对应用是唯一的。 在生成时,会使用约定 {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css
创建项目捆绑包,其中占位符 {STATIC WEB ASSETS BASE PATH}
是静态 Web 资产的基路径。
如果利用了其他项目(如 NuGet 包或Razor 类库 ),则捆绑的文件将发生以下情况:
使用 CSS 导入引用这些样式。
不会发布为使用这些样式的应用的静态 Web 资产。
利用 CSS 预处理器的变量、嵌套、模块、混合和继承等功能,可有效改进 CSS 开发。 虽然 CSS 隔离并不原生支持 CSS 预处理器(如 Sass 或 Less),但只要在生成过程中框架重写 CSS 选择器的步骤之前进行预处理器编译,就可以无缝集成 CSS 预处理器。 例如,使用 Visual Studio 将现有预处理器编译配置为 Visual Studio 任务运行程序资源管理器中的“生成前”任务。
许多第三方 NuGet 包(如 AspNetCore.SassCompiler
)都可以在生成过程开始时编译 SASS/SCSS 文件,再进行 CSS 隔离,而无需其他配置。
CSS 隔离允许在某些高级场景(例如依赖于现有工具或工作流)下进行配置。
在此部分中,{Pages|Views}
占位符为 Pages
Pages 应用的 Razor 或 MVC 应用的 Views
。
默认情况下,范围标识符使用格式 b-{STRING}
,其中 {STRING}
占位符是框架生成的十个字符的字符串。 若要自定义范围标识符格式,请将项目文件更新为所需模式:
<ItemGroup>
<None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
在上面的示例中,为 Index.cshtml.css
生成的 CSS 将其范围标识符从 b-{STRING}
更改为了 custom-scope-identifier
。
使用范围标识符来实现与限定范围的 CSS 文件的继承。 在下面的项目文件示例中,BaseView.cshtml.css
文件包含跨视图的通用样式。 DerivedView.cshtml.css
文件继承了这些样式。
<ItemGroup>
<None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
<None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
使用通配符 (*
) 运算符跨多个文件共享范围标识符:
<ItemGroup>
<None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
限定范围的 CSS 文件在应用的根目录生成。 在项目文件中,请使用 StaticWebAssetBasePath
属性来更改默认路径。 以下示例将作用域内 CSS 文件和应用的其余资产置于 _content
路径:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
若要禁用框架在运行时发布和加载限定范围的文件,请使用 DisableScopedCssBundling
属性。 使用此属性时,由其他工具或进程从 obj
目录中捕获隔离的 CSS 文件,并在运行时发布和加载这些文件:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Razor 类库 (RCL) 提供隔离的样式,<link>
标记的 href
属性指向 {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css
,其中占位符为:
{STATIC WEB ASSET BASE PATH}
:静态 Web 资产基路径。
{PACKAGE ID}
:库的包标识符 。 如果项目文件中未指定包标识符,则包标识符默认为项目的程序集名称。
如下示例中:
静态 Web 资产基路径为 _content/ClassLib
。
类库的程序集名称为 ClassLib
。
Pages/Shared/_Layout.cshtml
(Razor Pages) 或 Views/Shared/_Layout.cshtml
(MVC):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
有关 RCL 的详细信息,请参阅以下文章:
有关 Blazor CSS 隔离的信息,请参阅 ASP.NET Core Blazor CSS 隔离 。
使用 OnGet 处理程序回退来处理 HEAD 请求
HEAD
请求可检索特定资源的标头。 与 GET
请求不同,HEAD
请求不返回响应正文。
通常,针对 OnHead
请求创建和调用 HEAD
处理程序:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
如果未定义 Razor 处理程序,则 OnGet
Pages 会回退到调用 OnHead
处理程序。
Razor Pages 由 防伪造验证 保护。 FormTagHelper 将防伪造令牌注入 HTML 窗体元素。
将布局、分区、模板和标记帮助程序用于 Razor Pages
页面可使用 Razor 视图引擎的所有功能。 布局、分区、模板、标记帮助程序、_ViewStart.cshtml
和 _ViewImports.cshtml
的工作方式与它们在传统的 Razor 视图中的工作方式相同。
让我们使用其中的一些功能来整理此页面。
向 添加Pages/Shared/_Layout.cshtml
:
<!DOCTYPE html>
<html>
<head>
<title>RP Sample</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
<a asp-page="/Index">Home</a>
<a asp-page="/Customers/Create">Create</a>
<a asp-page="/Customers/Index">Customers</a> <br />
@RenderBody()
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>
布局 :
控制每个页面的布局(页面选择退出布局时除外)。
导入 HTML 结构,例如 JavaScript 和样式表。
调用 Razor 时,呈现 @RenderBody()
Page 的内容。
有关详细信息,请参阅布局页面 。
在 中设置 Pages/_ViewStart.cshtml
属性:
@{
Layout = "_Layout";
}
布局位于“页面/共享”文件夹中。 页面按层次结构从当前页面的文件夹开始查找其他视图(布局、模板、分区)。 可以从“Pages”文件夹下的任意 页面使用“Pages/Shared”文件夹中的布局。
布局文件应位于 Pages/Shared 文件夹中。
建议不要将布局文件放在“视图/共享”文件夹中。 视图/共享 是一种 MVC 视图模式。 Razor Pages 旨在依赖文件夹层次结构,而非路径约定。
Razor Page 中的视图搜索包含“Pages”文件夹。 用于 MVC 控制器和传统 Razor 视图的布局、模板和分区可正常运行。
添加 Pages/_ViewImports.cshtml
文件:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
本教程的后续部分中将介绍 @namespace
。 @addTagHelper
指令将内置标记帮助程序 引入“页面”文件夹中的所有页面。
页面上设置的 @namespace
指令:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
@namespace
指令将为页面设置命名空间。 @model
指令无需包含命名空间。
@namespace
中包含 _ViewImports.cshtml
指令后,指定的命名空间将为在导入 @namespace
指令的页面中生成的命名空间提供前缀。 生成的命名空间(后缀部分)的其余部分是包含 _ViewImports.cshtml
的文件夹与包含页面的文件夹之间的点分隔相对路径。
例如,PageModel
类 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 Page 生成的命名空间与 PageModel
类相同。
@namespace
也适用于传统 Razor 视图。
考虑 Pages/Customers/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
包含 Pages/Customers/Create.cshtml
的已更新的 _ViewImports.cshtml
视图文件和前面的布局文件:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
在前面的代码中,_ViewImports.cshtml
导入了命名空间和标记帮助程序。 布局文件导入了 JavaScript 文件。
Razor Pages 初学者项目 包含 Pages/_ValidationScriptsPartial.cshtml
,它与客户端验证联合。
有关分部视图的详细信息,请参阅 ASP.NET Core 中的分部视图 。
之前显示的 Create
页面使用 RedirectToPage
:
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
应用具有以下文件/文件夹结构:
/Pages
Index.cshtml
Privacy.cshtml
/Customers
Create.cshtml
Edit.cshtml
Index.cshtml
成功后,Pages/Customers/Create.cshtml
和 Pages/Customers/Edit.cshtml
页面将重定向到 Pages/Customers/Index.cshtml
。 字符串 ./Index
是用于访问前一页的相对页名称。 它用于生成 Pages/Customers/Index.cshtml
页面的 URL。 例如:
Url.Page("./Index", ...)
<a asp-page="./Index">Customers Index Page</a>
RedirectToPage("./Index")
绝对页名称 /Index
用于生成 Pages/Index.cshtml
页面的 URL。 例如:
Url.Page("/Index", ...)
<a asp-page="/Index">Home Index Page</a>
RedirectToPage("/Index")
页面名称是从根“/Pages”文件夹到页面的路径(包含前导 ,例如 /
)。 与硬编码 URL 相比,前面的 URL 生成示例提供了改进的选项和功能。 URL 生成使用路由 ,并且可以根据目标路径定义路由的方式生成参数并对参数编码。
页面的 URL 生成支持相对名称。 下表显示了 RedirectToPage
中不同的 Pages/Customers/Create.cshtml
参数选择的索引页。
展开表
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
参数与当前页的路径来计算目标页面的名称。
构建结构复杂的站点时,相对名称链接很有用。 如果使用相对名称链接文件夹中的页面:
重命名文件夹不会破坏相对链接。
链接不会中断,因为它们不包含文件夹名称。
若要重定向到不同区域 中的页面,请指定区域:
RedirectToPage("/Index", new { area = "Services" });
有关详细信息,请参阅 ASP.NET Core 中的区域 和 ASP.NET Core 中的 Razor Pages 路由和应用约定 。
可以通过 ViewDataAttribute 将数据传递到页面。 具有 [ViewData]
特性的属性从 ViewDataDictionary 保存和加载值。
在下面的示例中,AboutModel
将 [ViewData]
特性应用于 Title
属性:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
在“关于”页面中,以模型属性的形式访问 Title
属性:
<h1>@Model.Title</h1>
在布局中,从 ViewData 字典读取标题:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
ASP.NET Core 公开 TempData 。 此属性存储未读取的数据。 Keep 和 Peek 方法可用于检查数据,而不执行删除。 TempData
在多个请求需要数据的情况下对重定向很有用。
下面的代码使用 Message
设置 TempData
的值:
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
文件中的以下标记使用 Message
显示 TempData
的值。
<h3>Msg: @Model.Message</h3>
Pages/Customers/Index.cshtml.cs
页面模型将 [TempData]
属性应用到 Message
属性。
[TempData]
public string Message { get; set; }
有关详细信息,请参阅 TempData 。
以下页面使用 asp-page-handler
标记帮助程序为两个处理程序生成标记:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</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?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
前面的代码使用已命名处理程序方法。 已命名处理程序方法通过采用名称中 On<HTTP Verb>
之后及 Async
之前的文本(如果有)创建。 在前面的示例中,页面方法是 OnPostJoinList Async 和 OnPostJoinListUC Async。 删除 OnPost 和 Async 后,处理程序名称为 和 。
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
使用前面的代码时,提交到 OnPostJoinListAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH?handler=JoinList
。 提交到 OnPostJoinListUCAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH?handler=JoinListUC
。
使用 @page
指令,执行以下操作:
指定页面的自定义路由。 例如,使用 /Some/Other/Path
将“关于”页面的路由设置为 @page "/Some/Other/Path"
。
将段追加到页面的默认路由。 例如,可使用 @page "item"
将“item”段添加到页面的默认路由。
将参数追加到页面的默认路由。 例如,id
页面需要 ID 参数 @page "{id}"
。
支持开头处以波形符 (~
) 指定的相对于根目录的路径。 例如,@page "~/Some/Other/Path"
和 @page "/Some/Other/Path"
相同。
如果你不喜欢 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><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
使用前面的代码时,提交到 OnPostJoinListAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH/JoinList
。 提交到 OnPostJoinListUCAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH/JoinListUC
。
?
前面的 handler
表示路由参数为可选。
为页面和视图并置 JavaScript (JS) 文件是在应用中组织脚本的简便方法。
使用以下文件名扩展约定并置 JS 文件:
Razor Pages 应用的页面和 MVC 应用的视图:.cshtml.js
。 示例:
对于位于 Pages/Index.cshtml.js
的 Index
Pages 应用的 Razor 页面,使用 Pages/Index.cshtml
。
对于位于 Views/Home/Index.cshtml.js
的 MVC 应用的 Index
视图,使用 Views/Home/Index.cshtml
。
使用项目中文件的路径可以公开寻址并置的 JS 文件:
应用中并置脚本文件的页面和视图:
{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js
占位符 {PATH}
是页面、视图或组件的路径。
占位符 {PAGE, VIEW, OR COMPONENT}
是页面、视图或组件。
占位符 {EXTENSION}
与页面、视图或组件的扩展名匹配,为 razor
或 cshtml
。
Razor Pages 示例:
JS 页面的 Index
文件放置在 Pages
页面 (Pages/Index.cshtml.js
) 旁边的 Index
文件夹 (Pages/Index.cshtml
) 中。 在 Index
页面中,脚本在 Pages
文件夹中的路径引用:
@section Scripts {
<script src="~/Pages/Index.cshtml.js"></script>
}
可以将默认布局 Pages/Shared/_Layout.cshtml
配置为包含并置 JS 文件,无需单独配置每个页面:
<script asp-src-include="@(ViewContext.View.Path).js"></script>
示例下载 使用前面的代码片段在默认布局中包含并置 JS 文件。
发布应用后,框架会自动将脚本移动到 Web 根目录。 在前面的示例中,脚本将移动到 bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Pages\Index.cshtml.js
,其中 {TARGET FRAMEWORK MONIKER}
占位符是目标框架名字对象 (TFM) 。 无需更改 Index
页面中脚本的相对 URL。
发布应用后,框架会自动将脚本移动到 Web 根目录。 在前面的示例中,脚本将移动到 bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Components\Pages\Index.razor.js
,其中 {TARGET FRAMEWORK MONIKER}
占位符是目标框架名字对象 (TFM) 。 无需更改 Index
组件中脚本的相对 URL。
大多数应用不需要以下部分中的配置和设置。
要配置高级选项,请使用 AddRazorPages 重载,该重载配置 RazorPagesOptions :
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
使用 RazorPagesOptions 设置页面的根目录,或者为页面添加应用程序模型约定。 有关约定的详细信息,请参阅 Razor Pages 授权约定 .
若要预编译视图,请参阅 Razor 视图编译 。
默认情况下,Razor Pages 位于 /Pages 目录的根位置。 添加 WithRazorPagesAtContentRoot 以指定 Razor Pages 位于应用的内容根 (ContentRootPath ):
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
添加 WithRazorPagesRoot ,以指定 Razor Pages 位于应用中自定义根目录位置(提供相对路径):
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
在命令行中运行 dotnet new webapp
。
Razor 中已启用 Startup.cs
Pages:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
请考虑一个基本页面:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
前面的代码与具有控制器和视图的 ASP.NET Core 应用中使用的 Razor 视图文件 非常相似。 不同之处在于 @page
指令。 @page
使文件转换为一个 MVC 操作 ,这意味着它将直接处理请求,而无需通过控制器处理。 @page
必须是页面上的第一个 Razor 指令。 @page
会影响其他 Razor 构造的行为。 Razor Pages 文件名有 .cshtml
后缀。
将在以下两个文件中显示使用 PageModel
类的类似页面。 Pages/Index2.cshtml
文件:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Pages/Index2.cshtml.cs
页面模型:
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
按照惯例,PageModel
类文件的名称与追加 Razor 的 .cs
Page 文件名称相同。 例如,前面的 Razor Page 的名称为 Pages/Index2.cshtml
。 包含 PageModel
类的文件的名称为 Pages/Index2.cshtml.cs
。
页面的 URL 路径的关联由页面在文件系统中的位置决定。 下表显示了 Razor Page 路径及匹配的 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 Pages 文件。
URL 未包含页面时,Index
为默认页面。
由于 Razor Pages 的设计,在构建应用时可轻松实施用于 Web 浏览器的常用模式。 模型绑定 、标记帮助程序 和 HTML 帮助程序均只可用于 Page 类中定义的属性。 请参考为 Contact
模型实现的基本的“联系我们”窗体页面:
在本文档中的示例中,DbContext
在 Startup.cs 文件中进行初始化。
内存中数据库需要 Microsoft.EntityFrameworkCore.InMemory
NuGet 包。
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
services.AddRazorPages();
}
数据模型:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
数据库上下文:
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Models;
namespace RazorPagesContacts.Data
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
}
Pages/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
Pages/Create.cshtml.cs
页面模型:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using RazorPagesContacts.Models;
using System.Threading.Tasks;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
按照惯例,PageModel
类命名为 <PageName>Model
并且它与页面位于同一个命名空间中。
使用 PageModel
类,可以将页面的逻辑与其展示分离开来。 它定义了页面处理程序,用于处理发送到页面的请求和用于呈现页面的数据。 这种隔离可实现:
页面包含 OnPostAsync
处理程序方法 ,它在 POST
请求上运行(当用户发布窗体时)。 可以添加任何 HTTP 谓词的处理程序方法。 最常见的处理程序是:
OnGet
,用于初始化页面所需的状态。 在上面的代码中,OnGet
方法显示 CreateModel.cshtml
Razor Page。
OnPost
,用于处理窗体提交。
Async
命名后缀为可选,但是按照惯例通常会将它用于异步函数。 前面的代码通常用于 Razor Pages。
如果你熟悉使用控制器和视图的 ASP.NET 应用:
前面示例中的 OnPostAsync
代码类似于典型的控制器代码。
大多数 MVC 基元(例如模型绑定 、验证 和操作结果)通过控制器和通过 Razor Pages 的运作方式相同。
之前的 OnPostAsync
方法:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
OnPostAsync
的基本流:
检查验证错误。
如果没有错误,则保存数据并重定向。
如果有错误,则再次显示页面并附带验证消息。 很多情况下,都会在客户端上检测到验证错误,并且从不将它们提交到服务器。
Pages/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
Pages/Create.cshtml
中呈现的 HTML:
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
在前面的代码中,发布窗体:
Customer
属性使用 [BindProperty]
特性来选择加入模型绑定:
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
[BindProperty]
不应 用于包含不应由客户端更改的属性的模型。 有关详细信息,请参阅过度发布 。
Razor Pages 只绑定带有非 GET
谓词的属性。 如果绑定到属性,则无需通过编写代码将 HTTP 数据转换为模型类型。 绑定通过使用相同的属性显示窗体字段 (<input asp-for="Customer.Name">
) 来减少代码,并接受输入。
查看 Pages/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
在前面的代码中,输入标记帮助程序 <input asp-for="Customer.Name" />
将 HTML <input>
元素绑定到 Customer.Name
模型表达式。
使用 @addTagHelper
提供标记帮助程序。
Index.cshtml
是主页:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var contact in Model.Customer)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
关联的 PageModel
类 (Index.cshtml.cs
):
public class IndexModel : PageModel
{
private readonly CustomerDbContext _context;
public IndexModel(CustomerDbContext context)
{
_context = context;
}
public IList<Customer> Customer { get; set; }
public async Task OnGetAsync()
{
Customer = await _context.Customers.ToListAsync();
}
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customers.FindAsync(id);
if (contact != null)
{
_context.Customers.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
}
Index.cshtml
文件包含以下标记:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<a /a>
定位点标记帮助程序 使用 asp-route-{value}
属性生成“编辑”页面的链接。 此链接包含路由数据及联系人 ID。 例如 https://localhost:5001/Edit/1
。 标记帮助程序 使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素。
Index.cshtml
文件包含用于为每个客户联系人创建删除按钮的标记:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
呈现的 HTML:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
删除按钮采用 HTML 呈现,其 formaction 包括参数:
asp-route-id
属性指定的客户联系人 ID。
handler
属性指定的 asp-page-handler
。
选中按钮时,向服务器发送窗体 POST
请求。 按照惯例,根据方案 handler
基于 OnPost[handler]Async
参数的值来选择处理程序方法的名称。
因为本示例中 handler
是 delete
,因此 OnPostDeleteAsync
处理程序方法用于处理 POST
请求。 如果 asp-page-handler
设置为其他值(如 remove
),则选择名称为 OnPostRemoveAsync
的处理程序方法。
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customers.FindAsync(id);
if (contact != null)
{
_context.Customers.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
OnPostDeleteAsync
方法:
获取来自查询字符串的 id
。
使用 FindAsync
查询客户联系人的数据库。
如果找到客户联系人,则会将其删除,并更新数据库。
调用 RedirectToPage ,重定向到根索引页 (/Index
)。
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<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(未找到)错误。 若要使 ID 可选,请将 ?
追加到路由约束:
@page "{id:int?}"
Edit.cshtml.cs
文件:
public class EditModel : PageModel
{
private readonly CustomerDbContext _context;
public EditModel(CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Customer = await _context.Customers.FindAsync(id);
if (Customer == null)
{
return RedirectToPage("./Index");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw new Exception($"Customer {Customer.Id} not found!");
}
return RedirectToPage("./Index");
}
}
验证规则:
在模型类中以声明方式指定。
在应用中的所有位置强制执行。
System.ComponentModel.DataAnnotations 命名空间提供一组内置验证特性,可通过声明方式应用于类或属性。 DataAnnotations 还包含 [DataType]
等格式特性,有助于格式设置但不提供任何验证。
请考虑 Customer
模型:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
使用以下 Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
前面的代码:
包括 jQuery 和 jQuery 验证脚本。
使用 <div />
和 <span />
标记帮助程序 以实现:
则会生成以下 HTML:
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
<script src="/lib/jquery/dist/jquery.js"></script>
<script src="/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
如果在不使用名称值的情况下发布“创建”窗体,则将在窗体上显示错误消息“名称字段是必需的”。 如果客户端上已启用 JavaScript,浏览器会显示错误,而不会发布到服务器。
[StringLength(10)]
特性在呈现的 HTML 上生成 data-val-length-max="10"
。 data-val-length-max
阻止浏览器输入超过指定最大长度的内容。 如果使用 Fiddler 等工具来编辑和重播文章:
对于长度超过 10 的名称。
返回错误消息“‘名称’字段必须是最大长度为 10 的字符串。”
考虑下列 Movie
模型:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
验证特性指定要对应用这些特性的模型属性强制执行的行为:
Required
和 MinimumLength
特性表示属性必须有值,但用户可输入空格来满足此验证。
RegularExpression
特性用于限制可输入的字符。 在上述代码中,即“Genre”(分类):
只能使用字母。
第一个字母必须为大写。 不允许使用空格、数字和特殊字符。
RegularExpression
“Rating”(分级):
要求第一个字符为大写字母。
允许在后续空格中使用特殊字符和数字。 “PG-13”对“分级”有效,但对于“分类”无效。
Range
特性将值限制在指定范围内。
StringLength
特性可以设置字符串属性的最大长度,以及可选的最小长度。
从本质上来说,需要值类型(如 decimal
、int
、float
、DateTime
),但不需要 [Required]
特性。
Movie
模型的“创建”页面显示无效值错误:
有关详细信息,请参阅:
使用 OnGet 处理程序回退来处理 HEAD 请求
HEAD
请求可检索特定资源的标头。 与 GET
请求不同,HEAD
请求不返回响应正文。
通常,针对 OnHead
请求创建和调用 HEAD
处理程序:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
如果未定义 Razor 处理程序,则 OnGet
Pages 会回退到调用 OnHead
处理程序。
Razor Pages 由 防伪造验证 保护。 FormTagHelper 将防伪造令牌注入 HTML 窗体元素。
将布局、分区、模板和标记帮助程序用于 Razor Pages
页面可使用 Razor 视图引擎的所有功能。 布局、分区、模板、标记帮助程序、_ViewStart.cshtml
和 _ViewImports.cshtml
的工作方式与它们在传统的 Razor 视图中的工作方式相同。
让我们使用其中的一些功能来整理此页面。
向 添加Pages/Shared/_Layout.cshtml
:
<!DOCTYPE html>
<html>
<head>
<title>RP Sample</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
<a asp-page="/Index">Home</a>
<a asp-page="/Customers/Create">Create</a>
<a asp-page="/Customers/Index">Customers</a> <br />
@RenderBody()
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>
布局 :
控制每个页面的布局(页面选择退出布局时除外)。
导入 HTML 结构,例如 JavaScript 和样式表。
调用 Razor 时,呈现 @RenderBody()
Page 的内容。
有关详细信息,请参阅布局页面 。
在 中设置 Pages/_ViewStart.cshtml
属性:
@{
Layout = "_Layout";
}
布局位于“页面/共享”文件夹中。 页面按层次结构从当前页面的文件夹开始查找其他视图(布局、模板、分区)。 可以从“Pages”文件夹下的任意 页面使用“Pages/Shared”文件夹中的布局。
布局文件应位于 Pages/Shared 文件夹中。
建议不要将布局文件放在“视图/共享”文件夹中。 视图/共享 是一种 MVC 视图模式。 Razor Pages 旨在依赖文件夹层次结构,而非路径约定。
Razor Page 中的视图搜索包含“Pages”文件夹。 用于 MVC 控制器和传统 Razor 视图的布局、模板和分区可正常运行。
添加 Pages/_ViewImports.cshtml
文件:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
本教程的后续部分中将介绍 @namespace
。 @addTagHelper
指令将内置标记帮助程序 引入“页面”文件夹中的所有页面。
页面上设置的 @namespace
指令:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
@namespace
指令将为页面设置命名空间。 @model
指令无需包含命名空间。
@namespace
中包含 _ViewImports.cshtml
指令后,指定的命名空间将为在导入 @namespace
指令的页面中生成的命名空间提供前缀。 生成的命名空间(后缀部分)的其余部分是包含 _ViewImports.cshtml
的文件夹与包含页面的文件夹之间的点分隔相对路径。
例如,PageModel
类 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 Page 生成的命名空间与 PageModel
类相同。
@namespace
也适用于传统 Razor 视图。
考虑 Pages/Create.cshtml
视图文件:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
包含 Pages/Create.cshtml
的已更新的 _ViewImports.cshtml
视图文件和前面的布局文件:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
在前面的代码中,_ViewImports.cshtml
导入了命名空间和标记帮助程序。 布局文件导入了 JavaScript 文件。
Razor Pages 初学者项目 包含 Pages/_ValidationScriptsPartial.cshtml
,它与客户端验证联合。
有关分部视图的详细信息,请参阅 ASP.NET Core 中的分部视图 。
之前显示的 Create
页面使用 RedirectToPage
:
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
应用具有以下文件/文件夹结构:
/Pages
Index.cshtml
Privacy.cshtml
/Customers
Create.cshtml
Edit.cshtml
Index.cshtml
成功后,Pages/Customers/Create.cshtml
和 Pages/Customers/Edit.cshtml
页面将重定向到 Pages/Customers/Index.cshtml
。 字符串 ./Index
是用于访问前一页的相对页名称。 它用于生成 Pages/Customers/Index.cshtml
页面的 URL。 例如:
Url.Page("./Index", ...)
<a asp-page="./Index">Customers Index Page</a>
RedirectToPage("./Index")
绝对页名称 /Index
用于生成 Pages/Index.cshtml
页面的 URL。 例如:
Url.Page("/Index", ...)
<a asp-page="/Index">Home Index Page</a>
RedirectToPage("/Index")
页面名称是从根“/Pages”文件夹到页面的路径(包含前导 ,例如 /
)。 与硬编码 URL 相比,前面的 URL 生成示例提供了改进的选项和功能。 URL 生成使用路由 ,并且可以根据目标路径定义路由的方式生成参数并对参数编码。
页面的 URL 生成支持相对名称。 下表显示了 RedirectToPage
中不同的 Pages/Customers/Create.cshtml
参数选择的索引页。
展开表
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
参数与当前页的路径来计算目标页面的名称。
构建结构复杂的站点时,相对名称链接很有用。 如果使用相对名称链接文件夹中的页面:
重命名文件夹不会破坏相对链接。
链接不会中断,因为它们不包含文件夹名称。
若要重定向到不同区域 中的页面,请指定区域:
RedirectToPage("/Index", new { area = "Services" });
有关详细信息,请参阅 ASP.NET Core 中的区域 和 ASP.NET Core 中的 Razor Pages 路由和应用约定 。
可以通过 ViewDataAttribute 将数据传递到页面。 具有 [ViewData]
特性的属性从 ViewDataDictionary 保存和加载值。
在下面的示例中,AboutModel
将 [ViewData]
特性应用于 Title
属性:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
在“关于”页面中,以模型属性的形式访问 Title
属性:
<h1>@Model.Title</h1>
在布局中,从 ViewData 字典读取标题:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
ASP.NET Core 公开 TempData 。 此属性存储未读取的数据。 Keep 和 Peek 方法可用于检查数据,而不执行删除。 TempData
在多个请求需要数据的情况下对重定向很有用。
下面的代码使用 Message
设置 TempData
的值:
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
文件中的以下标记使用 Message
显示 TempData
的值。
<h3>Msg: @Model.Message</h3>
Pages/Customers/Index.cshtml.cs
页面模型将 [TempData]
属性应用到 Message
属性。
[TempData]
public string Message { get; set; }
有关详细信息,请参阅 TempData 。
以下页面使用 asp-page-handler
标记帮助程序为两个处理程序生成标记:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</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?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
前面的代码使用已命名处理程序方法。 已命名处理程序方法通过采用名称中 On<HTTP Verb>
之后及 Async
之前的文本(如果有)创建。 在前面的示例中,页面方法是 OnPostJoinList Async 和 OnPostJoinListUC Async。 删除 OnPost 和 Async 后,处理程序名称为 和 。
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
使用前面的代码时,提交到 OnPostJoinListAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH?handler=JoinList
。 提交到 OnPostJoinListUCAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH?handler=JoinListUC
。
使用 @page
指令,执行以下操作:
指定页面的自定义路由。 例如,使用 /Some/Other/Path
将“关于”页面的路由设置为 @page "/Some/Other/Path"
。
将段追加到页面的默认路由。 例如,可使用 @page "item"
将“item”段添加到页面的默认路由。
将参数追加到页面的默认路由。 例如,id
页面需要 ID 参数 @page "{id}"
。
支持开头处以波形符 (~
) 指定的相对于根目录的路径。 例如,@page "~/Some/Other/Path"
和 @page "/Some/Other/Path"
相同。
如果你不喜欢 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><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
使用前面的代码时,提交到 OnPostJoinListAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH/JoinList
。 提交到 OnPostJoinListUCAsync
的 URL 路径为 https://localhost:5001/Customers/CreateFATH/JoinListUC
。
?
前面的 handler
表示路由参数为可选。
大多数应用不需要以下部分中的配置和设置。
要配置高级选项,请使用 AddRazorPages 重载,该重载配置 RazorPagesOptions :
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
}
使用 RazorPagesOptions 设置页面的根目录,或者为页面添加应用程序模型约定。 有关约定的详细信息,请参阅 Razor Pages 授权约定 .
若要预编译视图,请参阅 Razor 视图编译 。
默认情况下,Razor Pages 位于 /Pages 目录的根位置。 添加 WithRazorPagesAtContentRoot 以指定 Razor Pages 位于应用的内容根 (ContentRootPath ):
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
}
添加 WithRazorPagesRoot ,以指定 Razor Pages 位于应用中自定义根目录位置(提供相对路径):
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
}