2022年8月10日:使用 ASP.NET Core 为初学者构建 Web 应用程序--使用 ASP.NET Core 创建 Web UI(没看懂需要再看一遍)

ASP.NET Core 支持使用名为 Razor 的本地模板化引擎创建网页。 本模块介绍了如何使用 Razor 和 ASP.NET Core 创建网页。

简介

通过在首选终端中运行以下命令,确保已安装 .NET 6.0:

dotnet --list-sdks

将显示类似于下面的输出:

确保列出了以 6 开头的版本。

了解使用Razor Pages的时机和原因

Razor Pages的涵义及其提供的益处

Razor Pages 是一种以页面为中心的服务器端编程模型,用于使用 ASP.NET Core 构建 Web UI。 Razor Pages 具有以下优点:

  • 只需使用 HTML、CSS 和 C# 的组合定义 UI 逻辑时,你可以轻松地开始构建动态 Web 应用。
  • 鼓励按功能组织文件,这样可简化对应用的维护。
  • 可以泛指为 HTML 文件,你可以按照过去习惯的方式在其中使用标记。 你还可以使用 Razor 语法添加服务器端 C# 代码。

Razor Pages 使用名为Razor 的标记语言将基于服务器的代码嵌入到网页中。 Razor 语法是 HTML 和 C# 的组合,其中的 C# 代码定义了页面的动态呈现逻辑。 在使用 Razor 语法的网页中,可以有两种类型的内容:客户端内容和服务器代码:

  • 客户端内容:包含你在网页中使用的内容:HTML 标记(元素)、样式信息(如 CSS)、可能存在的一些客户端脚本(如 JavaScript)和纯文本。
  • 服务器代码:使用 Razor 可以将服务器代码添加到客户端内容。 如果页面中包含服务器代码,则服务器会先运行该代码,然后再将页面发送到浏览器。 通过在服务器上运行,该代码可以执行比单独使用客户端内容更复杂的任务。 例如,可以安全访问数据库。 最重要的是,服务器代码可以动态创建客户端内容 - 它可以生成 HTML 标记或其他内容,并将其连同页面可能包含的任何静态 HTML 一起发送到浏览器。 从浏览器的角度来看,服务器上生成的客户端内容与任何其他客户端内容没有什么不同。

PageModel中的关注点分离

Razor Pages 在 C# PageModel 类中强制对与页面相关的数据属性和逻辑操作执行关注点分离。 模型对象通常定义数据属性,并封装与这些数据属性相关的任何逻辑或操作。 具体而言,PageModel :

  • 封装仅限于其 Razor 页面的数据属性和逻辑操作。
  • 为发送到页面的 HTTP 请求和用于呈现页面的数据定义页面处理程序。

使用Razor Pages的时机

在 ASP.NET Core 应用中使用 Razor Pages 的时机:

  • 需要从 ASP.NET Core 应用生成动态 Web UI。
  • 首选使用以页面为中心的方法来开发 Web 应用,其中的页面标记与 PageModel 会很接近。
  • 希望以页面为中心的 ASP.NET Core 应用使用分步视图 - 机制,以减少整个站点重复使用通用标记的次数。

使用 Razor Pages,可以通过更简单的方式来整理 ASP.NET Core 页面:

  • 在 Razor 页面 (PageModel) 中定义的所有视图(页面)特定逻辑和页面属性都可以一起保存在其自己的命名空间和目录中。
  • 相关页面的组可以保存在各自的命名空间和目录中。

ASP.NET Core 还支持使用MVC模式 生成 Web 应用。 模型定义应用及其组件的基本行为和数据。 视图使用 HTML 和 Razor 语法提供 UI。 控制器是接收 HTTP 请求并处理用户操作的类。

首选生成清晰地分离了模型、视图和控制器的 Web 应用时,请使用 ASP.NET Core MVC。

Razor Pages 的基础机构与 ASP.NET Core MVC 相同。 Razor Pages 和 MVC 都可以在同一 ASP.NET Core 应用中使用。 ASP.NET Core MVC 超出了此模块的范围。

创建新的ASP.NET Core应用

创建 ASP.NET Core Web 应用的最简单方法是使用 .NET CLI。 安装 .NET SDK 时,系统会预安装 CLI。

创建Web应用项目

为了设置 .NET 项目来与 Web 应用配合工作,你将使用 Visual Studio Code。 Visual Studio Code 包含一个集成终端,这使创建新项目变得简单。

在 Visual Studio Code 中,选择“文件”>“打开文件夹”。

在所选位置创建名为 RazorPagesPizza 的新文件夹,然后单击“选择文件夹”。 如果显示一个提示,询问“你是否信任此文件夹中文件的作者?”请选择 Yes 按钮。

从主菜单中选择“视图”>“终端”,以便从 Visual Studio Code 中打开集成终端。

在终端窗口中,输入以下命令。

dotnet new webapp -f net6.0

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows

PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\ASP.NET\RazorPagesPizza> dotnet new webapp -f net6.0

欢迎使用 .NET 6.0!
---------------------
SDK 版本: 6.0.303

遥测
---------
.NET 工具会收集用法数据,帮助我们改善你的体验。它由 Microsoft 收集并与社区共享。你可通过使用喜欢的 shell 将 DOTNET_CLI_TELEMETRY_OPTOUT 环境变量设置为 "1" 或 "true" 来选择退出遥测。

阅读有关 .NET CLI 工具遥测的更多信息: https://aka.ms/dotnet-cli-telemetry

----------------
已安装 ASP.NET Core HTTPS 开发证书。
若要信任该证书,请运行 "dotnet dev-certs https --trust" (仅限 Windows 和 macOS)。

正在处理创建后操作...
在 C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\ASP.NET\RazorPagesPizza\RazorPagesPizza.csproj 上运行 “dotnet restore”...
  正在确定要还原的项目…
  已还原 C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\ASP.NET\RazorPagesPizza\RazorPagesPizza.csproj (用时 104 ms)。
已成功还原。


PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\ASP.NET\RazorPagesPizza>

此命令创建基本 Web 应用项目的文件,以及名为 RazorPagesPizza.csproj 的 C# 项目文件。

Visual Studio Code 可能会提示你添加资产以调试项目。 单击对话框上的“Yes”。

此命令使用 ASP.NET Core 项目模板来创建基于 C# 的 Web 应用项目的基架。 此命令会创建 RazorPagesPizza 目录,其中包含在 .NET 上运行的 ASP.NET Core 项目。 项目名称与 RazorPagesPizza 目录名称匹配。

你现在应可以访问以下文件。

-| obj
-| Pages
   - | Shared
   - | _ViewImports.cshtml
   - | _ViewStart.cshtml
   - | Error.cshtml
   - | Error.cshtml.cs
   - | Index.cshtml
   - | Index.cshtml.cs
   - | Privacy.cshtml
   - | Privacy.cshtml.cs
-| Properties
-| wwwroot
-| appsettings.Development.json
-| appsettings.json
-| Program.cs
-| RazorPagesPizza.csproj

运行Razor Pages项目并浏览其UI

编译并运行该项目

在命令行界面中运行以下 .NET Core CLI 命令:

dotnet run

上述命令:

  • 在当前目录中找到项目文件。
  • 检索并安装此项目所需的任何项目依赖项。
  • 编译项目代码。
  • 使用 ASP.NET Core 的 Kestrel Web 服务器将 Web 应用托管在 HHTP 和 HTTPS 终结点中。

创建项目时,将为 HTTP 选择 5000-5300 端口,为 HTTPS 选择 7000-7300 端口。 与往常一样,通过编辑项目的 launchSettings.json 文件,可以轻松更改开发过程中使用的端口。 此模块使用以 https 开头的安全 localhost URL。

Now listening on: https://localhost:7192
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5211
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
  Content root path: /home/<user>/aspnet-learn/src/RazorPagesPizza

如果在自己的计算机上运行此应用,则可以将浏览器指向输出中显示的 HTTPS 链接(在上一种情况下为 https://localhost:7192)以查看生成的页面。

浏览应用

  1. 请注意终端输出中显示的 HTTP URL,例如 https://localhost:7192

  2. 通过浏览到 HTTPS URL,在浏览器中打开应用。

  3. 单击页面顶部导航栏中的链接,导航到“隐私”页。 请注意,URL 以“Privacy”结尾。 按照约定,Razor Pages 应用将页面路由映射到 Pages 目录结构中的文件。

  4. 通过按 Ctrl+C 键组合(在 Mac 上为 Command+C),停止应用程序。

已验证了可以成功编译、运行和部署项目。 让我们对其进行修改,以便用户可以显示和更新披萨列表。

浏览Razor Pages项目

了解项目

RazorPagesPizza 项目目录当前在 Visual Studio Code 编辑器中打开。 下表列出了需要注意的项目文件和目录。 在 Visual Studio Code 编辑器窗口中检查每一项目录。

名称说明
Pages/包含 Razor Pages 和支持文件。 每个 Razor 页面都是一对文件:
* 一个 .cshtml 文件,其中包含使用 Razor 语法且带有 C# 代码的标记。
* .cshtml.cs   PageModel 类文件,可定义页面处理程序方法和用于呈现页面的数据。
wwwroot/包含静态资产文件,例如 HTML、JavaScript 和 CSS。
RazorPagesPizza.csproj包含项目的配置元数据,例如依赖项。
Program.cs充当应用的托管入口点并配置应用行为,例如页面之间的路由。

Razor页面文件及其配对的PageModel类文件

按照约定,Page 目录是在 ASP.NET Core 应用中存储和组织所有 Razor Pages 的位置。

Razor 页面具有 .cshtml 文件扩展名。 按照约定,其关联的 PageModel C# 类文件使用相同的名称,但追加了 .cs。 例如,Razor 页面 Index.cshtml 的关联 PageModel 类文件是 Index.cshtml.cs。

模型对象定义数据属性,并封装与这些数据属性相关的逻辑或操作。 PageModel 在本质上与之相同,不过该模型更明确地封装数据属性和逻辑操作,使其作用域仅限于其 Razor 页面。 PageModel 类:

  • 支持从 Razor 页面展示的内容中分离其逻辑。
  • 为发送到页面的请求和用于呈现页面的数据定义页面处理程序。

页面处理程序是作为 HTTP 请求的结果执行的方法。 例如,Razor 页面的 PageModel 类中的 OnGet 方法将针对 HTTP GET 请求自动执行。

Pages/Shared目录

按照约定,在多个 Razor Pages 上共享的分部标记元素位于 Pages/Shared 目录中。 RazorPagesPizza 应用使用两个共享的分部视图,这些视图是在创建新的 ASP.NET Core Web 应用程序项目时包含的:

  • _Layout.cshtml:在多个页面中提供公共布局元素。
  • _ValidationScriptsPartial.cshtml:提供验证功能,如客户端窗体输入验证和跨站点防伪验证。 此分部视图可用于项目中的所有页面。

布局和分部视图文件

  • 布局:在 ASP.NET Core 中,布局是指 .cshtml 文件,用于为应用中的视图定义顶层模板。 应用不需要布局。 应用可以定义多个布局,其中不同的视图指定不同的布局。 大多数 Web 应用都有一个通用布局,可提供一致的用户体验。 布局通常包括常见的 UI 元素,如应用标头、导航或菜单元素以及页脚。 应用中的许多页面也经常使用常见的 HTML 结构,如脚本和样式表。 所有这些共享元素都可以在布局文件中定义,随后可由应用中使用的任何视图引用该文件。 布局可减少视图中的重复代码。

  • 分布视图:分部视图是 Razor 标记文件 (.cshtml),用于在另一个标记文件呈现的输出中呈现 HTML 输出。 分部视图可将大型标记文件拆分为较小的组件。 还可减少标记文件中常见标记内容的重复情况。 分部视图不可用于维护公共布局元素。 公共布局元素是在 _Layout.cshtml 文件中指定的。

Pages目录结构和路由请求

默认情况下,Razor Pages 在 Pages 目录中使用该目录结构作为路由请求的约定。 例如,位于 Pages 目录根目录中的索引页是应用站点的默认页面。 在 RazorPagesPizza 项目的 Pages/Products 目录中,可以找到一系列 Razor 页面,其中包括 Index.cshtml 页面。 例如,路由到 /Product/ 的请求将被定向,以使用实际位于例如 Products/Index.cshtml 的默认 Index.cshtml 页面。 为方便起见,该项目的 Razor 页面 (.cshtml) 和随附的 PageModel 类 (.cshtml.cs) 已归入到 Pages/Products。 所有传递的路由参数值都可以通过属性进行访问。 ASP.NET Core 提供强大的路由功能。

下表提供了此模块完成的项目要用到的路由。

URL映射到此 Razor 页面
www.domain.comPages/Index.cshtml
www.domain.com/indexPages/Index.cshtml
www.domain.com/productsPages/Products/Index.cshtml
www.domain.com/products/createPages/Products/Create.cshtml

添加带有窗体的披萨列表页

使用 Razor Pages 从页面呈现的内容中分离其逻辑,在 RazorPagesPizza 项目中创建一个窗体。

创建页面

RazorPagesPizza 项目目录当前在 Visual Studio Code 中打开。 在命令行界面中运行以下 .NET CLI 命令:

dotnet new page --name Pizza --namespace RazorPagesPizza.Pages --output Pages

上述命令:

  • 将在 RazorPagesPizza.Pages 命名空间中创建以下文件:
    • Pizza.cshtml—Razor 页面
    • Pizza.cshtml.cs—随附的 PageModel 类
  • 将两个文件存储在项目的 Pages 目录中。

请注意,使用 CLI 创建这些文件并没有什么神奇之处。 还可以手动创建文件;CLI 命令只是执行此操作的快捷方式。

了解Razor页面的结构

打开新的 Pages/Pizza.cshtml Razor 页面。 检查此文件的标记:

@page
 @model RazorPagesPizza.Pages.PizzaModel
 @{
 }

上面的 Razor 页面包含已保留的 Razor 关键字:

  • @page 指令将页面变成了 Razor 页面。 它表示页面可处理 HTTP 请求。 @page 指令必须是 Razor 页面上的第一个指令。
  • @model 指令指定可用于 Razor 页面的模型类型。 在此示例中,该类型是 PageModel 派生的类名,并将其命名空间作为前缀。 如之前所述,该类是在 Pages/Pizza.cshtml.cs 中定义的。

呈现HTML并转换为C#

以下标记是 @ 符号后跟 C# 代码的一个示例。 此代码将 ViewData 集合的 Title 键值设置为 Create。 Razor 语法使用 @ 符号从 HTML 转换为 C#。 如果 @ 符号后跟 Razor 保留关键字,则它会转换为 Razor 特定的标记;否则,它将转换为 C#。 Razor 计算 C# 表达式并将其呈现在 HTML 输出中。

@{
    ViewData["Title"] = "Pizza";
}

Razor 页面支持 Razor 语法,该语法将结合使用 HTML 和 C#。 C# 代码在服务器上定义页面的动态呈现逻辑。 默认的 Razor 语言为 HTML。 从 Razor 标记呈现 HTML 与从 HTML 文件呈现 HTML 没有什么不同。 服务器呈现的 .cshtml Razor 页面文件中的 HTML 标记并无变化。 在 Razor Pages 中,你可像往常一样使用 HTML。 与此同时,当你学习使用功能强大且省时的 Razor 功能后,即可利用这些功能。

添加Pizza和PizzaService类

在开始实现管理披萨的窗体之前,需要添加一个数据存储供执行操作。

我们需要使用模型类来表示库存中的披萨。 模型包含用于表示披萨特征的属性。 模型用于在 Web 应用中传递数据,以及在数据存储中保存披萨选项。 该数据存储是一个简单的本地内存中缓存服务。 在实际应用程序中,可以考虑将数据库(例如 SQL Server)与 Entity Framework Core 结合使用

创建披萨模型

在项目的根目录中运行以下命令,以创建 Models 文件夹:

mkdir Models

在 Visual Studio Code 中选择 Models 文件夹,并添加名为 Pizza.cs 的新文件。

项目根现在包含一个 Models 目录,其中包含一个空的 Pizza.cs 文件。 目录名称 Models 是一种约定。

将以下代码添加到 Models/Product.cs 以定义披萨。 保存所做更改。

using System.ComponentModel.DataAnnotations;

namespace RazorPagesPizza.Models;

public class Pizza
{
    public int Id { get; set; }

    [Required]
    public string? Name { get; set; }
    public PizzaSize Size { get; set; }
    public bool IsGlutenFree { get; set; }

    [Range(0.01, 9999.99)]
    public decimal Price { get; set; }
}

public enum PizzaSize { Small, Medium, Large }

添加数据服务

在项目的根目录中运行以下命令,以创建 Services 文件夹:

mkdir Services

在 Visual Studio Code 中选择该文件夹,并添加名为 PizzaService.cs 的新文件。

将以下代码添加到 Services/PizzaService.cs,以创建内存中披萨数据服务。 保存所做更改。

using RazorPagesPizza.Models;

namespace RazorPagesPizza.Services;
public static class PizzaService
{
    static List<Pizza> Pizzas { get; }
    static int nextId = 3;
    static PizzaService()
    {
        Pizzas = new List<Pizza>
                {
                    new Pizza { Id = 1, Name = "Classic Italian", Price=20.00M, Size=PizzaSize.    Large, IsGlutenFree = false },
                    new Pizza { Id = 2, Name = "Veggie", Price=15.00M, Size=PizzaSize.Small,     IsGlutenFree = true }
                };
    }

    public static List<Pizza> GetAll() => Pizzas;

    public static Pizza? Get(int id) => Pizzas.FirstOrDefault(p => p.Id == id);

    public static void Add(Pizza pizza)
    {
        pizza.Id = nextId++;
        Pizzas.Add(pizza);
    }

    public static void Delete(int id)
    {
        var pizza = Get(id);
        if (pizza is null)
            return;

        Pizzas.Remove(pizza);
    }

    public static void Update(Pizza pizza)
    {
        var index = Pizzas.FindIndex(p => p.Id == pizza.Id);
        if (index == -1)
            return;

        Pizzas[index] = pizza;
    }
                }

此服务提供具有两种披萨的简单的内存中数据缓存服务,默认情况下,我们的 Web 应用将使用这两种披萨进行演示。 如果先停止再启动 Web 应用,内存中数据缓存将重置为 PizzaService 的构造函数中的两种默认披萨。

将窗体标记添加到“披萨”Razor页面

将 Pages/Pizza.cshtml 的内容替换为以下标记。 保存所做更改。

@page
@using RazorPagesPizza.Models
@model RazorPagesPizza.Pages.PizzaModel
@{
    ViewData["Title"] = "Pizza List";
}

<h1>Pizza List 🍕</h1>
<form method="post" class="card p-3">
    <div class="row">
        <div asp-validation-summary="All"></div>
    </div>
    <div class="form-group mb-0 align-middle">
        <label asp-for="NewPizza.Name">Name</label>
        <input type="text" asp-for="NewPizza.Name" class="mr-5">
        <label asp-for="NewPizza.Size">Size</label>
        <select asp-for="NewPizza.Size" asp-items="Html.GetEnumSelectList<PizzaSize>()" class="mr-5"></select>
        <label asp-for="NewPizza.Price"></label>
        <input asp-for="NewPizza.Price" class="mr-5" />
        <label asp-for="NewPizza.IsGlutenFree">Gluten Free</label>
        <input type="checkbox" asp-for="NewPizza.IsGlutenFree" class="mr-5">
        <button class="btn btn-primary">Add</button>
    </div>
</form>
<table class="table mt-5">
    <thead>
        <tr>
            <th scope="col">Name</th>
            <th scope="col">Price</th>
            <th scope="col">Size</th>
            <th scope="col">Gluten Free</th>
            <th scope="col">Delete</th>
        </tr>
    </thead>
    @foreach (var pizza in Model.pizzas)
    {
        <tr>
            <td>@pizza.Name</td>
            <td>@($"{pizza.Price:C}")</td>
            <td>@pizza.Size</td>
            <td>@Model.GlutenFreeText(pizza)</td>
            <td>
                <form method="post" asp-page-handler="Delete" asp-route-id="@pizza.Id">
                    <button class="btn btn-danger">Delete</button>
                </form>
            </td>
        </tr>
    }
</table>

@section Scripts {
<partial name="_ValidationScriptsPartial" />
}

“披萨”Razor 页面使用 HTML 和 Razor 来支持产品创建窗体。 该窗体接受要创建的产品的“名称”和“价格”值。 凭借相对较少的标记,动态功能已通过 Razor 标记帮助程序提供。 客户端窗体输入验证通过包含 Pages/Shared/_ValidationScriptsPartial.cshtml 分部视图启用。 该分部视图的内容将注入到布局页的 Scripts 部分。

了解Razor标记帮助程序

在 HTML 和 C# 之间进行上下文切换会降低效率,标记帮助程序解决了此问题。 ASP.NET Core 内置的多数标记帮助程序都可扩展标准 HTML 元素。 标记帮助程序为 HTML 元素提供了额外的服务器端特性,使元素更加可靠。

此页面上有四个标记帮助程序:

  • 部分
  • Label
  • 输入
  • 验证摘要消息

分部标记帮助程序

以下标记使用分部标记帮助程序异步呈现分部视图:

<partial name="_ValidationScriptsPartial" />

部分标记帮助程序的 name 属性接受不含文件扩展名的分部视图名称。 视图发现进程将在项目中查找分部视图。

此标记帮助器语法可替代下面的 HTML 帮助程序语法:

@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}

标签标记帮助程序

下面的标记使用标签标记帮助程序:

<label asp-for="NewPizza.Name" class="control-label"></label>

标签标记帮助程序将扩展标准 HTML <label> 元素。 与许多标记帮助程序一样,它使用 asp-for 特性。 该特性接受指定的 PageModel 属性。 PageModelName 属性的值将作为 HTML <label> 元素的内容呈现。 asp-for 特性的作用域限制在 Razor 页面的 PageModel,因此不使用 @ 符号。 正如此处所需,标记是动态的,但会保持简洁且易于添加到标记中。

输入标记帮助程序

下面的标记使用输入标记帮助程序。 它扩展标准 HTML <input> 元素。 它还使用 asp-for 特性来指定 PageModel 属性。

<input asp-for="NewPizza.Name" class="form-control" />

输入标记帮助程序:

  • 与标签标记帮助程序类似,评估 NewPizza.Name 属性。
  • 基于该属性添加 id 和 name HTML 属性。
  • 适当地设置输入类型。 例如,如果指定的属性类型为 bool,则 checkbox 输入类型将用于生成的 HTML。 本例中,NewPizza.Name 属性类型为 string。 NewPizza.Name 属性是由模型的数据注释特性设置的,我们稍后将在本模块讨论这些特性。
  • 基于通过 PageModel 提供的模型数据注释特性使用 jQuery 提供客户端验证。
  • 如果客户端验证成功,则提示 Razor 引擎提供其他更可靠的服务器端验证。 本模块稍后将逐步演示“披萨”Razor 页面的 HTTP POST 事件生命周期(包括客户端和服务器端输入验证)。

下列 HTML 输出是从“披萨”页面中的输入标记帮助程序生成的:

<input name="NewPizza.Name" class="form-control" id="NewPizza_Name" type="text" value="" data-val-required="The Name field is required." data-val="true">

验证摘要标记帮助程序

以下标记使用验证摘要标记帮助程序。 它显示模型上单个属性的验证消息。

<div asp-validation-summary="All"></div>

输入标记帮助程序会将 HTML5 data- 特性添加到输入元素。 这些特定基于 C# 模型类中的属性。 发生响应式客户端验证时,还会在服务器上执行验证(这更安全)。

验证摘要标记帮助程序将呈现以下 HTML:

<input name="NewPizza.Price" class="form-control" id="NewPizza_Price" type="text" value="" data-val-required="The Price field is required." data-val="true" data-val-range-min="0.01" data-val-range-max="9999.99" data-val-range="The field Price must be between 0.01 and 9999.99." data-val-number="The field Price must be a number.">

typedata-val-range-mindata-val-range-max 和错误响应是由模型的 Product.Price 属性的数据注释动态设置的。

“产品创建”Razor 页面已创建。 为了处理窗体交互,需要修改 PageModel 类来处理窗体的 HTTP POST 请求。 接下来,让我们来探讨 PageModel 交互。

使用PageModel处理程序方法处理交互

了解Razor Pages PageModel类的结构

新的 PageModel 类文件包含以下 C# 代码:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesPizza.Pages
{
    public class PizzaModel : PageModel
    {
        public void OnGet()
        {
        }
    }
}

Razor 页面的 PageModel 类文件为发送到页面的 HTTP 请求定义任何页面处理程序,以及定义用于呈现页面的数据。 PageModel 从 Razor 页面分离这些关注点,使应用的模块化水平更高,更易于维护。 按照约定,PageModel 类命名为 [PageName]Model 并与 Razor 页面位于同一个命名空间中。 PizzaModel 类位于 RazorPagesPizza.Pages 的命名空间中。

PizzaModel 类使用空 OnGet 页面处理程序处理 HTTP GET 请求。 可以为任何 HTTP 谓词添加处理程序。 最常见的处理程序是:

  • OnGet,用于初始化页面所需的状态。
  • OnPost,用于处理窗体提交。

“披萨”页面包含一个窗体,因此需要一个 HTTP POST 页面处理程序。

PageModel中的HTTP POST页面处理程序

OnPostAsync 页面处理程序需要为此应用执行以下任务:

  • 验证发送到 PageModel 的用户提交数据是否有效。
  • 如果尝试的 PageModel 更改无效,则再次向用户显示“披萨”页面。 会显示一条阐明输入要求的消息。
  • 如果 PageModel 更新有效,则会将数据更改传递到名为 PizzaService 的服务。 PizzaService 将处理保留数据的问题。

绑定模型

PizzaModel 类需要 Pizza 模型的访问权限。 它将验证并传递“披萨”窗体中的 Pizza 条目。 该操作通过使用以下代码中的 [BindProperty] 特性来实现:

[BindProperty]
public Pizza NewPizza { get; set; }

绑定到属性可以减少必须编写的代码量。 绑定通过使用相同的属性来呈现字段(例如在 <input asp-for="Pizza.Name"> 中),从而减少代码。

使用ASP.NET Core数据注释的内置服务器端模型验证

创建 ASP.NET Core Web 应用时提供了模型绑定和验证。 两者都在执行页面处理程序之前自动发生。 因此 OnPostAsync 处理程序仅需确定验证的结果。

if (!ModelState.IsValid)
{
    return Page();
}

在上述代码中,ModelState 表示模型绑定和验证中的错误。 如果 ModelState 无效,则再次向用户显示“披萨”页面。 上一单元已介绍“披萨”Razor 页面如何使用 ASP.NET Core 的内置客户端窗体输入验证来向用户迅速提供输入验证反馈。

如果 ModelState 有效,OnPostAsync 页面处理程序将调用 PizzaService 的实例。 PizzaService 负责存储信息 - 使用内存中数据存储。

使用数据注释为披萨模型定义验证规则

此项目使用一个中心模型文件 Pizza.cs 进行 Pizza 模型验证和操作。 它可供 UI 中涉及的所有 Razor 页面 PageModels 用于执行 Pizza CRUD 操作,并可用于验证从 Web API 接收的“披萨”数据。 按照约定,它存储在 Models 目录中。 Pizza 模型的命名空间是 RazorPagesPizza.Models

新 PizzaModel 类通过以下 using 指令获取对 RazorPagesPizza.Models 命名空间中定义的任何模型类型(包括 Pizza 模型)的访问权限:

using RazorPagesPizza.Models;

检查 Pizza 模型类:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesPizza.Models
{
    public class Pizza
    {
        public int Id { get; set; }

        [Required]
        public string Name { get; set; }
        public PizzaSize Size { get; set; }
        public bool IsGlutenFree { get; set; }

        [Range(0.01, 9999.99)]
        public decimal Price { get; set; }
    }

    public enum PizzaSize { Small, Medium, Large }
}

数据注释是用于指定所需行为的特性,这些行为将对其适用的模型属性强制执行。

Pizza 类使用:

  • [Required] 特性指示属性必须具有值。
  • [Range] 特性,用于将值限定为特定范围。

如果要强制实施更多验证规则,可以轻松地在一个位置(Pizza 模型)中修改特性,无需修改项目中的任何 PageModel 类文件。 这是一项显著的优势!

System.ComponentModel.DataAnnotations 中提供了一组全面的数据注释特性。 

作为数据传输对象的披萨模型

Pizza 模型同样作为数据传输对象 (DTO) 。 DTO 是一个对象,定义将通过网络发送的数据,数据发往 Web API。 在此应用程序的更高级版本中,RazorPagesPizza 项目的 PizzaService 类将使用 Pizza 模型作为 DTO,该 DTO 定义了有效的“披萨”数据,在 Web API 或后备数据库可以收发这些数据。

接下来,将更新与 PizzaService 类的交互 PizzaModel 以列出现有的披萨并创建新的披萨。

处理披萨窗体提交

将为“披萨”Razor 页面窗体添加 HTTP POST 页面处理程序。 最后将逐步完成 Pizza 模型及其用于驱动客户端和服务器端验证的数据注释。

了解Razor Pages PageModel类的结构

打开 Pages/Pizza.cshtml.cs PageModel 类文件。 你可能还记得,在创建名为“披萨”的新 Razor 页面时,生成了名为 Pizza.cshtml.cs 的 PageModel 类文件。 检查内容。 其中包含以下 C# 代码:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace RazorPagesPizza.Pages
{
    public class PizzaModel : PageModel
    {
        public void OnGet()
        {
        }
    }
}

更新HTTP Get页面处理程序以显示披萨的列表

现在,PizzaModel 类使用空 OnGet 页面处理程序处理 HTTP GET 请求。 让我们更新它以显示 PizzaService 中的披萨列表。

更新OnGetd页面处理程序

更新 OnGet 方法,如下所示:

public void OnGet()
{
    pizzas = PizzaService.GetAll();
}

将一个名为 pizzas 的 List<Pizza> 变量添加到 PizzaModel 类中:

public List<Pizza> pizzas = new();

调用 OnGet 方法时,它会将 PizzaService.GetAll() 方法的结果分配给 pizzas 变量。 Razor 页面模板将可以访问此变量,此变量将写入到列出可用披萨的表中。

这些语句引用了 PizzaService 和 Pizza 类,因此你需要将以下 using 语句添加到 PizzaModel 类的顶部:

using RazorPagesPizza.Models;
using RazorPagesPizza.Services;

使用实用工具方法设置列表中的无麸质信息的格式

IsGlutenFree 属性是一个布尔值。 可使用实用工具方法将布尔值设置为字符串格式。 将以下实用工具方法添加到 PizzaModel 类:

public string GlutenFreeText(Pizza pizza)
{
    if (pizza.IsGlutenFree)
        return "Gluten Free";
    return "Not Gluten Free";
}

将 HTTP POST 页面处理程序添加到 PageModel

将以下方法添加到 Pages/Pizza.cshtml.cs PageModel 类。 保存所做更改。

public IActionResult OnPost()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }
    PizzaService.Add(NewPizza);
    return RedirectToAction("Get");
}

PizzaModel 类现在有一个异步 OnPost 页面处理程序。 当用户发布“披萨”页面的窗体时,OnPost 将执行。 还可以使用可选的异步命名后缀 OnPostAsync

OnPost 页面处理程序需要为此应用执行以下任务:

  • 验证发送到 PageModel 的用户提交数据是否有效。
  • 如果尝试的 PageModel 更改无效,则再次向用户显示“披萨”页面。 会显示一条阐明输入要求的消息。
  • 如果 PageModel 更新有效,则会将数据更改传递到名为 PizzaService 的服务。 PizzaService 将处理 HTTP 请求的关注点并对 Web API 做出响应。

绑定模型

PizzaModel 类需要 Pizza 模型的访问权限。 它将使用 [BindProperty] 属性验证并传递“披萨”窗体中的 Pizza 条目。 将以下代码添加到 PizzaModel 类:

[BindProperty]
public Pizza NewPizza { get; set; } = new();

为“删除”按钮添加HTTP POST处理程序

Razor 页可以包含多个窗体。 由于披萨列表中的“删除”按钮会修改数据,因此需要 HTTP POST 而不是 HTTP GET。

将下面的 OnPostDelete 方法添加到 PizzaModel 类:

public IActionResult OnPostDelete(int id)
{
    PizzaService.Delete(id);
    return RedirectToAction("Get");
}

链接到“披萨”页面

“披萨”页面已创建并实现。 接下来让用户导航到此页面。

将定位标记帮助程序添加到“索引”页

在 Pages/Shared/Layout. cshtml 中,将以下代码添加到 Home 和 Privacy 链接之间的 <nav> 部分:

<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-page="/Pizza">Pizza List</a>
</li>

结果将如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - RazorPagesPizza</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">RazorPagesPizza</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Pizza">Pizza List</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - RazorPagesPizza - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

保存所做更改。

上面突出显示的代码使用定位标记帮助程序。 标记帮助程序:

  • 将用户定向到 Pages/Pizzas/Create.cshtml Razor 页面,该页面与“索引”页面位于同一目录中。
  • 通过添加自定义 HTML 属性(如 asp-page-handler)增强标准 HTML 定位 (<a>) 标记。

asp-page-handler 属性用于路由到 asp-page 属性中定义的 Razor 页面的特定页面处理程序。 asp-page 属性用于将定位标记的 href 属性值设置为特定 Razor 页面。

  1. 在命令行界面中运行以下 .NET CLI 命令:

    dotnet run
  2. 通过浏览到 HTTPS URL,在浏览器中打开应用。

  3. 单击导航栏中的 Pizza List 链接,导航到新“披萨”页。

  4. 填写表单以创建新的披萨,然后单击 Add 按钮。 你应看到新的披萨出现在披萨列表中。

  5. 通过输入无效的披萨信息(如空白披萨名称或者 10000 或更高的价格)来测试验证。 你应看到显示的验证错误消息。

  6. 单击 Delete 按钮,从列表中删除披萨。 你应看到披萨从列表中消失。

     

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值