假设你是一家名为 Contoso Pizza 的披萨公司的雇员。 经理要求你为公司内部管理网站开发一个必备组件 - 披萨库存管理页面。 生成的应用应使视图和数据模型关注点分离。
在本模块中,你将:
本模块末尾提供了一些链接,可以通过其中的内容更深入地了解我们介绍的各个功能区域。
本模块使用 .NET CLI 和 Visual Studio Code(Windows、Linux 和 macOS)来演示 ASP.NET Core Razor Pages 开发。 完成本模块后,可以使用 Visual Studio (Windows)、Visual Studio for Mac (macOS) 或 Visual Studio Code 等开发环境来应用其概念。
提示
可以根据 IDE 使用 GitHub Codespaces 跳过安装以下工具。 在另一个浏览器选项卡中,导航到包含此模块的初学者应用的 GitHub 存储库,选择“代码”按钮,并在 main
分支上创建新的 codespace。 有关详细信息,请参阅创建 Codespace。
本单元介绍在 ASP.NET Core 应用中使用 Razor Pages 的时机和原因。
Razor Pages 是一种以页面为中心的服务器端编程模型,用于使用 ASP.NET Core 构建 Web UI。 优点包括:
Razor Pages 使用 Razor 将基于服务器的代码嵌入到网页中。 Razor 语法结合了 HTML 和 C# 来定义动态呈现逻辑。 这意味着可以在 HTML 标记中使用 C# 变量和方法,以在运行时在服务器上生成动态 Web 内容。 请务必了解 Razor Pages 不能替代 HTML、CSS 或 JavaScript。 它们是结合这些技术创建动态 Web 内容的一种方式。
Razor Pages 强制使用 C# PageModel
类分离关注点,封装范围限定为其 Razor 页面的数据属性和逻辑运算,并为 HTTP 请求定义页面处理程序。 PageModel
类是由 ASP.NET Core 项目模板自动生成的分部类。 PageModel
类位于 Pages
文件夹中,以 Razor 页命名。 例如,Index.cshtml
Razor 页的 PageModel
类名为 IndexModel.cs
。
在 ASP.NET Core 应用中使用 Razor Pages 的时机:
Razor Pages 通过将相关页面及其逻辑保存在其自己的命名空间和目录中来简化 ASP.NET Core 页面组织。
备注
ASP.NET Core 还支持使用模型视图控制器 (MVC) 模式来生成 web 应用。 如果希望在模型、视图和控制器之间实现清晰的分隔,可以使用 MVC。 Razor Pages 和 MVC 可以共存在同一应用中。 MVC 超出了本模块的范畴。
Razor Pages 在工作效率方面的优势是,它可将应用中的视图的更改集中在一起。
让我们花一些时间来熟悉 ContosoPizza 文件夹中的现有代码。 该项目是使用 dotnet new webapp
命令创建的 ASP.NET Core Web 应用。 下面描述了你的团队成员所做的更改。
Pizza
类。PizzaContext
类。 它继承自 Entity Framework Core 中的 DbContext
类。 Entity Framework Core 是一种对象关系映射器 (ORMObject Relational Mapping:是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用的–“虚拟对象数据库”。),可便于更轻松地使用数据库。PizzaService
类,该类公开用于列出和添加披萨的方法。PizzaService
类使用 PizzaContext
类在数据库中读取和写入披萨。Entity Framework Core 也生成了一些内容:
项目中的所有其他内容与创建项目时的内容保持一致。 下表描述了由 dotnet new webapp
命令生成的项目结构。
名称 | 说明 |
---|---|
Pages/ | 包含 Razor Pages 和支持文件。 每个 Razor 页面都有一个 .cshtml 文件和一个 .cshtml.csPageModel 类文件。 |
wwwroot/ | 包含静态资产文件,例如 HTML、JavaScript 和 CSS。 |
ContosoPizza.csproj | 包含项目配置元数据,例如依赖项。 |
Program.cs | 充当应用的入口点并配置应用行为,例如路由。 |
其他值得注意的观察内容:
Razor 页面文件及其配对的 PageModel
类文件
Razor 页面存储在 Pages 目录中。 如上所述,每个 Razor 页面都有一个 .cshtml 文件和一个 .cshtml.csPageModel
类文件。 PageModel
类允许分离 Razor 页面的逻辑和表示形式,定义针对请求的页面处理程序,并封装范围限定为其 Razor 页面的数据属性和逻辑。
Pages 目录结构和路由请求
Razor Pages 使用 Pages 目录结构作为路由请求的约定。 下表显示了 URL 如何映射到文件名:
URL | 映射到 Razor 页面 |
---|---|
www.domain.com |
Pages/Index.cshtml |
www.domain.com/Index |
Pages/Index.cshtml |
www.domain.com/Privacy |
Pages/Privacy.cshtml |
www.domain.com/Error |
Pages/Error.cshtml |
Pages 目录中的子文件夹用于组织 Razor 页面。 例如,如果有 Pages/Products 目录,则 URL 将反映该结构:
URL | 映射到 Razor 页面 |
---|---|
www.domain.com/Products |
Pages/Products/Index.cshtml |
www.domain.com/Products/Index |
Pages/Products/Index.cshtml |
www.domain.com/Products/Create |
Pages/Products/Create.cshtml |
布局和其他共享文件
有多个文件可跨多个页面共享。 这些文件确定常见的布局元素和页面导入。 下表描述了每个文件的用途。
文件 | 说明 |
---|---|
_ViewImports.cshtml | 导入跨多个页面使用的命名空间和类。 |
_ViewStart.cshtml | 指定所有 Razor 页面的默认布局。 |
Pages/Shared/_Layout.cshtml | 这是 _ViewStart.cshtml 文件指定的布局。 在多个页面中实现公共布局元素。 |
Pages/Shared/_ValidationScriptsPartial.cshtml | 为所有页面提供验证功能。 |
让我们运行该项目,这样我们可以看到它的运行情况。
dotnet watch
此命令:
IDE 会提示你在浏览器中打开应用。 选择“在浏览器中打开”。
将呈现的主页与 IDE 中的 Pages/Index.cshtml 进行比较:
观察文件中 HTML、Razor 语法和 C# 代码的组合。
@
字符表示。@{ }
块中。 记下文件顶部的指令:@page
指令用于指定此文件是 Razor 页面。@model
指令用于指定页面的模型类型(在此示例中为 IndexModel
,这是在 Pages/Index.cshtml.cs 中定义的)。查看 C# 代码块。
ViewData
字典中 Title
项的值设置为“主页”。ViewData
字典用于在 Razor 页面和 IndexModel
类之间传递数据。Title
值用于设置页面的
元素。使应用在终端窗口中保持运行。 我们将在即将介绍的单元中使用它。 还需保留包含运行中的 Contoso Pizza 应用的浏览器选项卡。 你将对应用进行更改,然后浏览器将自动刷新以显示更改。
让我们对登陆页面进行少量更改,使其与披萨应用具有更强的相关性。
在 Pages/Index.cshtml 中,将 C# 代码块中的代码替换为以下代码:
ViewData["Title"] = "The Home for Pizza Lovers";
TimeSpan timeInBusiness = DateTime.Now - new DateTime(2018, 8, 14);
前面的代码:
将 ViewData 字典中 Title 项的值设置为“披萨爱好者的天堂”。
计算自业务开业以来已过去的时间。
按如下所示修改 HTML:
将 元素替换为以下代码:
Welcome to Contoso Pizza
将 The best pizza in town for @Convert.ToInt32(timeInBusiness.TotalDays) days!
元素替换为以下代码:
前面的代码:
将标题更改为“欢迎光临 Contoso Pizza”。
显示自业务开业以来已过去的天数。
@ 字符用于从 HTML 切换到 Razor 语法。
Convert.ToInt32 方法用于将 timeInBusiness 变量的 TotalDays 属性转换为整数。
Convert 类是 System 命名空间的一部分,该命名空间由 ContosoPizza.csproj 文件中的
元素自动导入。
保存文件。 包含应用的浏览器选项卡会自动刷新以显示更改。 如果使用的是 GitHub Codespaces,则文件会自动保存,但需要手动刷新浏览器选项卡。
若要创建新的 Razor 页面,需使用 .NET CLI。
由于终端被 dotnet watch 命令阻止,因此请通过右键单击“资源管理器”中的 ContosoPizza 文件夹以打开另一个终端,然后选择“在集成终端中打开”。
在终端窗口中,输入以下命令:
dotnet new page --name PizzaList --namespace ContosoPizza.Pages --output Pages
上述命令:
将在ContosoPizza.Pages
命名空间中创建以下文件:
PizzaList.cshtml - Razor 页面
PizzaList.cshtml.cs - 随附的 PageModel
类
将两个文件存储在项目的 Pages 子目录中。
@{ }
代码块中添加以下代码: ViewData["Title"] = "Pizza List ";
Pizza List
这会向页面添加一个标题,以及两个 HTML 注释占位符,用于稍后将添加的功能。
保存文件。 如果使用的是 GitHub Codespaces,则文件会自动保存。
返回到正在运行 dotnet watch
的终端,然后按 Ctrl+R 重新加载应用并检测新文件。
这是测试页面的好时机,但你还无法进行测试,因为该页面未链接到导航菜单。 你现在需要执行该操作。
打开 Pages/Shared/_Layout.cshtml。
在包含 navbar-nav
类的
元素中(从第 21 行开始),请注意包含指向“主页”和“隐私”页面的链接的 元素。 将以下代码添加到列表末尾处包含“隐私”链接的
元素的后面:
这会向导航菜单添加一个指向 PizzaList 页面的链接。
保存文件。 包含应用的浏览器选项卡会自动刷新以显示更改。 如果使用的是 GitHub Codespaces,则文件会自动保存,但需要手动刷新浏览器选项卡。
在导航菜单中选择“披萨列表 ”链接。 随即会显示“披萨列表”页面。
“披萨列表”页依赖于 PizzaService
对象来检索披萨列表。 你将使用依赖项注入向页面提供 PizzaService
对象。 为完成此操作,必须向容器注册 PizzaService
类。
打开 Program.cs。
在将服务添加到容器的部分中,添加以下代码:
builder.Services.AddScoped<PizzaService>();
此代码向依赖项注入容器注册 PizzaService
类。 AddScoped
方法指示应为每个 HTTP 请求创建新的 PizzaService
对象。 现在可以将 PizzaService
注入到任何 Razor 页面。
保存文件。 如果使用的是 GitHub Codespaces,则文件会自动保存。
让我们来修改“披萨列表”页面的 PageModel
类,以从 PizzaService
对象检索披萨列表并将其存储在属性中。
打开 Pages/PizzaList.cshtml.cs。
将下列 using
语句添加到文件顶部:
using ContosoPizza.Models;
using ContosoPizza.Services;
这些语句可导入你将在页面中使用的 Pizza
和 PizzaService
类型。
在 ContosoPizza.Pages
命名空间块中,将整个 PizzaListModel
类替换为以下代码:
public class PizzaListModel : PageModel
{
private readonly PizzaService _service;
public IList<Pizza> PizzaList { get;set; } = default!;
public PizzaListModel(PizzaService service)
{
_service = service;
}
public void OnGet()
{
PizzaList = _service.GetPizzas();
}
}
在上述代码中:
_service
的专用只读PizzaService
。 此变量将保存对PizzaService
对象的引用。
readonly
关键字指示在构造函数中设置 _service
变量的值之后无法对其进行更改。PizzaList
属性以保存披萨列表。
IList
类型指示 PizzaList
属性将保存 Pizza
对象列表。PizzaList
被初始化为 default!
,以向编译器表明它稍后将被初始化,因此不需要进行 null 安全性检查。PizzaService
对象。
PizzaService
通过依赖项注入提供。OnGet
方法以从 PizzaService
对象中检索披萨列表并将其存储在 PizzaList
属性中。提示
如果需要了解 null 安全方面的帮助,请参阅 C# 中的 null 安全。
保存文件。 如果使用的是 GitHub Codespaces,则文件会自动保存。
返回到正在运行 dotnet watch
的终端,然后按 Ctrl+R 以重新加载具有已注册服务和 PizzaListModel
的新构造函数的应用。
现在,可在页面上访问披萨列表,接下来需要使用该列表在页面上显示披萨。
注释:
Name
Price
Size
Gluten Free
Delete
@foreach (var pizza in Model.PizzaList)
{
@pizza.Name
@($"{pizza.Price:C}")
@pizza.Size
@(pizza.IsGlutenFree ? "✔️" : string.Empty)
}
在上述代码中: