ASP.NET Core in Action 13 Creating a website with Razor Pages

Part 3 Generating HTML with Razor Pages and MVC

第 3 部分:使用 Razor Pages 和 MVC 生成 HTML

In parts 1 and 2 we looked in detail at how to create JSON API applications using minimal APIs. You learned how to configure your app from multiple sources, how to use dependency injection to reduce coupling in your app, and how to document your APIs with OpenAPI.

在第 1 部分和第 2 部分中,我们详细介绍了如何使用最少的 API 创建 JSON API 应用程序。您学习了如何从多个来源配置应用程序,如何使用依赖关系注入来减少应用程序中的耦合,以及如何使用 OpenAPI 记录 API。

API apps are everywhere these days. Mobile apps use them; clients-side Single Page Applications (SPAs) like Angular, React, or Blazor use them; even other apps use them for server-to-server communication. But in many cases, you don’t need separate server-side and client-side apps. Instead, you could create a server-rendered app.

如今,API 应用程序无处不在。移动应用程序使用它们;Angular、React 或 Blazor 等客户端单页应用程序 (SPA) 使用它们;甚至其他应用程序也使用它们进行服务器到服务器的通信。但在许多情况下,您不需要单独的服务器端和客户端应用程序。相反,您可以创建服务器呈现的应用程序。

With server-rendering, your application generates the HTML on the server and the browser displays this directly in the browser; no extra client-side framework required. You can still add dynamic client-side behavior using JavaScript, but fundamentally each page in your app is a standalone request and response, which gives a simpler developer experience.

使用服务器呈现时,应用程序会在服务器上生成 HTML,浏览器会直接在浏览器中显示 HTML;不需要额外的客户端框架。您仍然可以使用 JavaScript 添加动态客户端行为,但从根本上说,应用程序中的每个页面都是一个独立的请求和响应,这提供了更简单的开发人员体验。

In part 3, you’ll learn about the Razor Pages and MVC frameworks used by ASP.NET Core to create server-rendered apps. In chapters 13 through 16 we’ll examine the behavior of the Razor Pages framework itself, routing, and model binding. In chapters 17 and 18 we’ll look at how you can build the UI for your application using the Razor syntax and Tag Helpers, so that users can navigate and interact with your app.

在第 3 部分中,您将了解 ASP.NET Core 用于创建服务器渲染应用的 Razor Pages 和 MVC 框架。在第 13 章到第 16 章中,我们将研究 Razor Pages 框架本身的行为、路由和模型绑定。在第 17 章和第 18 章中,我们将介绍如何使用 Razor 语法和标记帮助程序为应用程序构建 UI,以便用户可以导航应用并与之交互。

In chapter 19 you’ll learn how to use the MVC framework directly, instead of Razor Pages. You’ll learn how to use MVC controllers to build server-rendered apps and when to choose MVC controllers instead of Razor Pages. In chapter 20 you’ll learn to how to use MVC controllers to build API applications, as an alternative to minimal APIs. Finally, in chapters 21 and 22 you’ll learn how to refactor your apps to extract common code out of your Razor Pages and API controllers using filters.

在第 19 章中,您将学习如何直接使用 MVC 框架,而不是 Razor Pages。您将学习如何使用 MVC 控制器构建服务器呈现的应用程序,以及何时选择 MVC 控制器而不是 Razor Pages。在第 20 章中,您将学习如何使用 MVC 控制器来构建 API 应用程序,作为最小 API 的替代方案。最后,在第 21 章和第 22 章中,您将学习如何重构应用程序,以使用筛选器从 Razor Pages 和 API 控制器中提取常见代码。

13 Creating a website with Razor Pages
13 使用 Razor Pages 创建网站

This chapter covers

本章涵盖

  • Getting started with Razor Pages
    Razor Pages 入门

  • Introducing Razor Pages and the Model-View-Controller (MVC) design pattern
    Razor Pages 和模型-视图-控制器 (MVC) 设计模式简介

  • Using Razor Pages in ASP.NET Core
    在 ASP.NET Core 中使用 Razor Pages

So far in this book you’ve built one type of ASP.NET Core application: minimal API apps that return JavaScript Object Notation (JSON). In this chapter you’ll learn how to build server-rendered, page-based applications using Razor Pages. Most ASP.NET Core apps fall into one of three categories:

到目前为止,在本书中,您已经构建了一种类型的 ASP.NET Core 应用程序:返回 JavaScript 对象表示法 (JSON) 的最小 API 应用程序。在本章中,您将学习如何使用 Razor Pages 构建服务器渲染的、基于页面的应用程序。大多数 ASP.NET Core 应用程序都属于以下三类之一:

  • An API designed for consumption by another machine or in code—Web apps often serve as an API to backend server processes, to a mobile app, or to a client framework for building single-page applications (SPAs). In this case your application serves data in machine-readable formats such as JSON or Extensible Markup Language (XML) instead of the human-focused HTML output.
    供其他计算机使用或在代码中使用的 API — Web 应用程序通常用作后端服务器进程、移动应用程序或用于构建单页应用程序 (SPA) 的客户端框架的 API。在这种情况下,您的应用程序以机器可读格式(如 JSON 或可扩展标记语言 (XML))而不是以人类为中心的 HTML 输出提供数据。

  • An HTML web application designed for direct use by users—If the application is consumed directly by users, as in a traditional web application, Razor Pages is responsible for generating the web pages that the user interacts with. It handles requests for URLs, receives data posted via forms, and generates the HTML that enables users to view and navigate your app.
    专为用户直接使用而设计的 HTML Web 应用程序 – 如果应用程序由用户直接使用,就像在传统 Web 应用程序中一样,Razor Pages 负责生成用户与之交互的网页。它处理对 URL 的请求,接收通过表单发布的数据,并生成使用户能够查看和导航您的应用程序的 HTML。

  • Both an HTML web application and an API—It’s also possible to have applications that serve both needs, which can let you cater to a wider range of clients while sharing logic in your application.
    HTML Web 应用程序和 API — 也可以拥有同时满足这两种需求的应用程序,这样您就可以在应用程序中共享逻辑的同时满足更广泛的客户端需求。

In this chapter you’ll learn how ASP.NET Core uses Razor Pages to handle the second of these options: creating server-side rendered HTML pages. We’ll get started quickly, using a template to create a simple Razor Pages application and comparing the features of a Razor Pages app with the minimal API apps you’ve seen so far. In section 13.2 we look at a more complex example of a Razor Page.

在本章中,你将了解 ASP.NET Core 如何使用 Razor Pages 来处理第二个选项:创建服务器端呈现的 HTML 页面。我们将快速入门,使用模板创建简单的 Razor Pages 应用程序,并将 Razor Pages 应用的功能与你目前看到的最小 API 应用进行比较。在 Section 13.2 中,我们看一个更复杂的 Razor Page 示例。

Next, we take a step back in section 13.3 to look at the MVC design pattern. I discuss some of the benefits of using this pattern, and you’ll learn why it’s been adopted by so many web frameworks as a model for building maintainable applications.

接下来,我们在 Section 13.3 中后退一步,看看 MVC 设计模式。我将讨论使用此模式的一些好处,您将了解为什么它被如此多的 Web 框架用作构建可维护应用程序的模型。

In section 13.4 you’ll learn how the MVC design pattern applies to ASP.NET Core. The MVC pattern is a broad concept that can be applied in a variety of situations, but the use case in ASP.NET Core is specifically as a UI abstraction. You’ll see how Razor Pages implements the MVC design pattern and builds on top of the ASP.NET Core MVC framework.

在第 13.4 节中,您将了解 MVC 设计模式如何应用于 ASP.NET Core。MVC 模式是一个广泛的概念,可以应用于各种情况,但 ASP.NET Core 中的用例专门用作 UI 抽象。您将了解 Razor Pages 如何实现 MVC 设计模式并在 ASP.NET Core MVC 框架之上构建。

In this chapter I’ll try to prepare you for each of the upcoming topics, but you may find that some of the behavior feels a bit like magic at this stage. Try not to become too concerned about exactly how all the Razor Pages pieces tie together yet; focus on the specific concepts being addressed and how they tie into concepts you’ve already met. We’ll start by creating a Razor Pages app to explore.

在本章中,我将尝试让您为即将到来的每个主题做好准备,但您可能会发现,在这个阶段,某些行为感觉有点像魔术。尽量不要太关心所有 Razor Pages 作品究竟是如何联系在一起的;专注于要解决的具体概念以及它们如何与您已经遇到的概念相关联。首先,我们将创建一个 Razor Pages 应用进行探索。

13.1 Your first Razor Pages application

13.1 您的第一个 Razor Pages 应用程序

In this section you’ll get started with Razor Pages by creating a new application from a template. After you’ve created the app and had a look around, we’ll look at some of the similarities and differences compared with a minimal API application. You’ll learn about the extra middleware added in the default template, look at how HTML is generated by Razor Pages, and take a look at the Razor Page equivalent of minimal API endpoint handlers: page handlers.

在本部分中,你将通过从模板创建新应用程序来开始使用 Razor Pages。在您创建应用程序并环顾四周后,我们将了解与最小 API 应用程序相比的一些相似之处和不同之处。您将了解默认模板中添加的额外中间件,了解 Razor Pages 如何生成 HTML,并查看 Razor Page 等价的最小 API 端点处理程序:页面处理程序。

13.1.1 Using the Web Application template

13.1.1 使用 Web 应用程序模板

Using a template is a quick way to get an application running, so we’ll take that approach using the ASP.NET Core Web App template. To create a Razor Pages application in Visual Studio, perform the following steps:

使用模板是运行应用程序的一种快速方法,因此我们将使用 ASP.NET Core 来采用这种方法Web App 模板。要在 Visual Studio 中创建 Razor Pages 应用程序,请执行以下步骤:

  1. Choose Create a New Project from the splash screen or choose File > New > Project from the main Visual Studio screen.
    从初始屏幕中选择 Create a New Project,或从 Visual Studio 主屏幕中选择 File > New > Project。

  2. From the list of templates, choose ASP.NET Core Web App, ensuring you select the C# language template.
    从模板列表中,选择 ASP.NET Core Web App,确保选择 C# 语言模板。

  3. On the next screen, enter a project name, location, and solution name, and click Next. You might use WebApplication1 as both the project and solution name, for example.
    在下一个屏幕上,输入项目名称、位置和解决方案名称,然后单击 Next。例如,您可以使用 WebApplication1 作为项目和解决方案名称。

  4. On the following screen (figure 13.1), do the following:
    在以下屏幕(图 13.1)中,执行以下作:

  • Select .NET 7.0. If this option isn’t available, ensure that you have .NET 7 installed. See appendix A for details on configuring your environment.
    选择 .NET 7.0。如果此选项不可用,请确保您已安装 .NET 7。有关配置环境的详细信息,请参阅附录 A。
  • Ensure that Configure for HTTPS is checked.
    确保选中 Configure for HTTPS (为 HTTPS 配置)。
  • Ensure that Enable Docker is unchecked.
    确保 Enable Docker (启用 Docker) 处于未选中状态。
  • Ensure that Do not use top-level statements is unchecked.
    确保 Do Not Use Top-level Statements 未选中。
  • Choose Create.
    选择 Create (创建)。

alt text

Figure 13.1 The additional information screen. This screen follows the Configure Your New Project dialog box and lets you customize the template that generates your application.
图 13.1 附加信息屏幕。此屏幕位于 Configure Your New Project 对话框之后,允许您自定义生成应用程序的模板。

If you’re not using Visual Studio, you can create a similar template using the .NET command-line interface (CLI). Create a folder to hold your new project. Open a PowerShell or cmd prompt in the folder (on Windows) or a terminal session (on Linux or macOS), and run the commands in the following listing.

如果不使用 Visual Studio,则可以使用 .NET 命令行界面 (CLI) 创建类似的模板。创建一个文件夹来保存您的新项目。在文件夹(在 Windows 上)或终端会话(在 Linux 或 macOS 上)中打开 PowerShell 或 cmd 提示符,然后运行以下列表中的命令。

Listing 13.1 Creating a new Razor Page application with the .NET CLI
清单 13.1 使用 .NET CLI创建新的 Razor Page 应用程序

dotnet new sln -n WebApplication1     ❶
dotnet new razor -o WebApplication1   ❷
dotnet sln add WebApplication1        ❸

❶ Creates a solution file called WebApplication1 in the current folder
在当前文件夹中创建名为 WebApplication1 的解决方案文件
❷ Creates an ASP.NET Core Razor Pages project in a subfolder, WebApplication1
在子文件夹 WebApplication1 中创建 ASP.NET Core Razor Pages 项目
❸ Adds the new project to the solution file
将新项目添加到解决方案文件中

Whether you use Visual Studio or the .NET CLI, now you can build and run your application. Press F5 to run your app using Visual Studio, or use dotnet run in the project folder. This command opens the appropriate URL in a web browser and displays the basic Welcome page, shown in figure 13.2.

无论您使用的是 Visual Studio 还是 .NET CLI,现在都可以构建和运行应用程序。按 F5 使用 Visual Studio 运行应用,或使用项目文件夹中的 dotnet run。此命令在 Web 浏览器中打开相应的 URL,并显示基本的 Welcome 页面,如图 13.2 所示。

alt text

Figure 13.2 The output of your new Razor Pages application. The template chooses a random port to use for your application’s URL, which is opened automatically in the browser when you run the app.
图 13.2 新 Razor Pages 应用程序的输出。该模板选择一个随机端口用于应用程序的 URL,当您运行应用程序时,该端口会自动在浏览器中打开。

By default, this page shows a simple Welcome banner and a link to the official Microsoft documentation for ASP.NET Core. At the top of the page are two links: Home and Privacy. The Home link is the page you’re currently on. Clicking Privacy takes you to a new page, shown in figure 13.3. As you’ll see in section 13.1.3, you can use Razor Pages in your application to define these two pages and build the HTML they display.

默认情况下,此页面显示一个简单的欢迎横幅和一个指向 ASP.NET Core 的官方 Microsoft 文档的链接。页面顶部有两个链接:Home 和 Privacy。Home link (主页) 链接是您当前所在的页面。点击 Privacy 将带你到一个新页面,如图 13.3 所示。如第 13.1.3 节所示,您可以在应用程序中使用 Razor Pages 来定义这两个页面并构建它们显示的 HTML。

alt text

Figure 13.3 The Privacy page of your application. You can navigate between the two pages of the application using the Home and Privacy links in the application’s header. The app generates the content of the pages using Razor Pages.
图 13.3 应用程序的 Privacy 页面。您可以使用应用程序标题中的 Home 和 Privacy 链接在应用程序的两个页面之间导航。该应用使用 Razor Pages 生成页面内容。

At this point, you should notice a couple of things:
此时,您应该注意以下几点:

  • The header containing the links and the application title, WebApplication1, is the same on both pages.
    包含链接和应用程序标题 WebApplication1 的标题在两个页面上是相同的。

  • The title of the page, as shown in the tab of the browser, changes to match the current page. You’ll see how to achieve these features in chapter 17, when we discuss the rendering of HTML using Razor templates.
    页面的标题(如浏览器选项卡中所示)会更改以匹配当前页面。您将在第 17 章中了解如何实现这些功能,届时我们将讨论使用 Razor 模板呈现 HTML。

There isn’t any more to the user experience of the application at this stage. Click around a little, and when you’re happy with the behavior of the application, return to your editor, and look at the files included in the template.
在此阶段,应用程序的用户体验不再有任何变化。单击一下,当您对应用程序的行为感到满意时,返回到编辑器,并查看模板中包含的文件。

This Razor Pages app has much the same structure as the minimal API applications you’ve created throughout this book, as shown in figure 13.4. The overall structure is identical apart from two extra folders you haven’t seen before:
此 Razor Pages 应用程序的结构与您在本书中创建的最小 API 应用程序大致相同,如图 13.4 所示。除了两个您以前从未见过的额外文件夹外,整体结构是相同的:

  • Pages folder—This folder contains the Razor Pages files that define the various pages in your web app, including the Home and Privacy pages you’ve already seen.
    Pages 文件夹 - 此文件夹包含 Razor Pages 文件,这些文件定义 Web 应用程序中的各个页面,包括您已经看到的 Home 和 Privacy 页面。

  • wwwroot folder—This folder is special in that it’s the only folder in your application that browsers are allowed to access directly when browsing your web app. You can store your Cascading Style Sheets (CSS), JavaScript, images, or static HTML files here, and the static file middleware will serve them to browsers when requested. The template creates subfolders inside wwwroot, but you don’t have to use them; you can structure your static files however you want inside wwwroot.
    wwwroot 文件夹 - 此文件夹很特殊,因为它是应用程序中唯一允许浏览器在浏览 Web 应用程序时直接访问的文件夹。您可以在此处存储级联样式表 (CSS)、JavaScript、图像或静态 HTML 文件,静态文件中间件将在需要时将它们提供给浏览器。该模板会在 wwwroot 中创建子文件夹,但您不必使用它们;您可以在 wwwroot 中根据需要构建静态文件。

alt text

Figure 13.4 Comparing the project structure of a minimal API app with a Razor Pages app. The Razor Pages app contains all the same files and folders, as well as the Pages folder for the Razor Page definitions and the wwwroot file for static files that are served directly to the browser.
图 13.4 比较最小 API 应用与 Razor Pages 应用的项目结构。Razor Pages 应用包含所有相同的文件和文件夹,以及 Razor Page 定义的 Pages 文件夹以及 wwwroot 文件,用于直接提供给浏览器的静态文件。

Aside from these extra files, the only other difference between a Razor Pages app and a minimal API app is the Program.cs file. In section 13.1.2 you’ll see that the Razor Pages app uses the same basic structure in Program.cs but adds the extra services and middleware used in a typical Razor Pages app.

除了这些额外的文件之外,Razor Pages 应用和最小 API 应用之间的唯一其他区别是 Program.cs 文件。在第 13.1.2 节中,你将看到 Razor Pages 应用在 Program.cs 中使用相同的基本结构,但添加了典型 Razor Pages 应用中使用的额外服务和中间件。

13.1.2 Adding and configuring services

13.1.2 添加和配置服务

One of the nice things about working with ASP.NET Core applications is that the setup code is quite similar even for completely different application models. No matter whether you’re creating a Razor Pages application or using minimal APIs, your Program.cs contains the same six steps:

使用 ASP.NET Core 应用程序的一个好处是,即使对于完全不同的应用程序模型,设置代码也非常相似。无论您是创建 Razor Pages 应用程序还是使用最少的 API,您的 Program.cs 都包含相同的六个步骤:

  1. Create a WebApplicationBuilder instance.
    创建 WebApplicationBuilder 实例。

  2. Register the required services with the WebApplicationBuilder.
    将所需的服务注册到WebApplicationBuilder 的 Web 应用程序构建器。

  3. Call Build on the builder instance to create a WebApplication instance.
    在构建器实例上调用 Build() 以创建一个WebApplication 实例。

  4. Add middleware to the WebApplication to create a pipeline.
    将中间件添加到 WebApplication 以创建管道。

  5. Map the endpoints in your application.
    映射应用程序中的终端节点。

  6. Call Run() on the WebApplication to start the server and handle requests.
    在 WebApplication 上调用 Run() 以启动服务器并处理请求。

The following listing shows the Program.cs file for the Razor Pages app. This file uses a lot more middleware than you’ve seen previously, but the overall structure should be familiar.

以下清单显示了 Razor Pages 应用的 Program.cs 文件。此文件使用的中间件比您之前看到的要多得多,但整体结构应该很熟悉。

Listing 13.2 The Program.cs file for a Razor Pages app
列表 13.2 Razor Pages 应用程序的 Program.cs 文件

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();    ❶

WebApplication app = builder.Build();

if (!app.Environment.IsDevelopment())   ❷
{   ❷
    app.UseExceptionHandler("/Error");  ❷
    app.UseHsts()
}   ❷

app.UseHttpsRedirection();    ❸
app.UseStaticFiles();    ❸
app.UseRouting();     ❸
app.UseAuthorization();    ❸

app.MapRazorPages();    ❹

app.Run();

❶ Registers the required services to use the Razor Pages feature
注册使用 Razor Pages 功能所需的服务
❷ Conditionally adds middleware depending on the runtime environment
根据运行时环境有条件地添加中间件
❸ Additional middleware can be added to the middleware pipeline.
以将其他中间件添加到中间件管道中。
❹ Registers each Razor Page as an endpoint in your application
将每个 Razor 页面注册为应用程序中的终结点

In chapter 4 you learned about middleware and the importance of ordering when adding middleware to the pipeline. This example adds six pieces of middleware to the pipeline, two of which are added only when not running in development:
在第 4 章中,您了解了中间件以及将中间件添加到管道时排序的重要性。此示例将 6 个中间件添加到pipeline 中,其中两个仅在未在 development 中运行时添加:

  • ExceptionHandlerMiddleware—You learned about this middleware in chapters 4 and 5. This middleware catches exceptions thrown by middleware later in the pipeline and generates a friendly error page.
    ExceptionHandlerMiddleware — 您在第 4 章和第 5 章中了解了此中间件。此中间件稍后在管道中捕获中间件引发的异常,并生成一个友好的错误页面。

  • HstsMiddleware—This middleware sets security headers in your response, in line with industry best practices. See chapter 28 for details about it and other security-related middleware.
    HstsMiddleware — 此中间件根据行业最佳实践在响应中设置安全标头。有关它和其他与安全相关的中间件的详细信息,请参见 Chapter 28。

  • HttpsRedirectionMiddleware—This middleware ensures that your application responds only to secure (HTTPS) requests and is an industry best practice. We’ll look at HTTPS in chapter 28.
    HttpsRedirectionMiddleware — 此中间件可确保您的应用程序仅响应安全 (HTTPS) 请求,是行业最佳实践。我们将在第 28 章中介绍 HTTPS。

  • StaticFileMiddleware—As you saw in chapter 4, this middleware serves requests for static files (such as .css and .js files) from the wwwroot folder in your app.
    StaticFileMiddleware — 如第 4 章所示,此中间件为应用程序中 wwwroot 文件夹中的静态文件(如 .css 和 .js 文件)的请求提供服务。

  • RoutingMiddleware—The routing middleware is responsible for selecting the endpoint for an incoming request. WebApplication adds it by default, but as discussed in chapter 4, adding it explicitly ensures that it runs after the StaticFileMiddleware.
    RoutingMiddleware — 路由中间件负责为传入请求选择终端节点。 WebApplication 默认会添加它,但正如第 4 章所讨论的,显式添加它可以确保它在 StaticFileMiddleware 之后运行。

  • AuthorizationMiddleware—This middleware controls whether an endpoint is allowed to run based on the user making the request, but requires you also to configure authentication for your application. You’ll learn more about authentication in chapter 23 and authorization in chapter 24.
    AuthorizationMiddleware — 此中间件根据发出请求的用户控制是否允许终端节点运行,但还要求您为应用程序配置身份验证。您将在第 23 章中了解有关身份验证的更多信息,在第 24 章中了解有关授权的更多信息。

In addition to the middleware added explicitly, WebApplication automatically adds some extra middleware (as discussed in chapter 4), such as the EndpointMiddleware, which is automatically added to the end of the middleware pipeline. As with minimal APIs, the RoutingMiddleware selects which endpoint handler to execute, and the EndpointMiddleware executes the handler to generate a response.
除了显式添加的中间件之外,WebApplication 还会自动添加一些额外的中间件(如第 4 章所述),例如 EndpointMiddleware,它会自动添加到中间件管道的末尾。与最小 API 一样,RoutingMiddleware 选择要执行的端点处理程序,而 EndpointMiddleware 执行处理程序以生成响应。

Together, this pair of middleware is responsible for interpreting the request to determine which Razor Page to invoke, for reading parameters from the request, and for generating the final HTML. Little configuration is required; you need only add the middleware to the pipeline and specify that you want to use Razor Page endpoints by calling MapRazorPages. For each request, the routing middleware uses the request’s URL to determine which Razor Page to invoke. Then the endpoint middleware executes the Razor Page to generate the HTML response.

这对中间件共同负责解释请求以确定要调用的 Razor Page、从请求中读取参数以及生成最终 HTML。几乎不需要配置;您只需将中间件添加到管道,并通过调用 MapRazorPages 指定要使用 Razor Page 端点。对于每个请求,路由中间件使用请求的 URL 来确定要调用的 Razor Page。然后,终结点中间件执行 Razor Page 以生成 HTML 响应。

When the application is configured, it can start handling requests. But how does it handle them? In section 13.1.3 you’ll get a glimpse at Razor Pages and how they generate HTML.

配置应用程序后,它可以开始处理请求。但是它是如何处理的呢?在第 13.1.3 节中,您将了解 Razor Pages 以及它们如何生成 HTML。

13.1.3 Generating HTML with Razor Pages

13.1.3 使用 Razor Pages 生成 HTML

When an ASP.NET Core application receives a request, it progresses through the middleware pipeline until a middleware component handles it. Normally, the routing middleware matches a request URL’s path to a configured route, which defines which Razor Page to invoke, and the endpoint middleware invokes it.

当 ASP.NET Core 应用程序收到请求时,它会在中间件管道中前进,直到中间件组件处理该请求。通常,路由中间件将请求 URL 的路径与配置的路由匹配,该路由定义要调用的 Razor Page,然后终结点中间件调用它。

Razor Pages are stored in .cshtml files (a portmanteau of .cs and .html) within the Pages folder of your project. In general, the routing middleware maps request URL paths to a single Razor Page by looking in the Pages folder of your project for a Razor Page with the same path. If you look back at figure 13.3, for example, you see that the Privacy page of your app corresponds to the path /Privacy in the browser’s address bar. If you look inside the Pages folder of your project, you’ll find the Privacy.cshtml file, shown in the following listing.

Razor Pages 存储在项目的 Pages 文件夹中的 .cshtml 文件(.cs 和 .xhtml 的组合)中。通常,路由中间件通过在项目的 Pages 文件夹中查找具有相同路径的 Razor 页面,将请求 URL 路径映射到单个 Razor 页面。例如,如果你回头看一下图 13.3,你会看到你的应用程序的 Privacy 页面对应于浏览器地址栏中的路径 /Privacy。如果查看项目的 Pages 文件夹,您将找到 Privacy.cshtml 文件,如以下清单所示。

Listing 13.3 The Privacy.cshtml Razor Page
列表 13.3 Privacy.cshtml Razor 页面

@page       ❶
@model PrivacyModel         ❷
@{
    ViewData["Title"] = "Privacy Policy";       ❸
}
<h1>@ViewData["Title"]</h1>     ❹

<p>Use this page to detail your site's privacy policy.</p>    ❺

❶ Indicates that this is a Razor Page
表示这是一个 Razor 页面
❷ Links the Razor Page to a specific PageModel
将 Razor 页面链接到特定的 PageModel
❸ C# code that doesn’t write to the response
不写入响应的 C# 代码
❹ HTML with dynamic C# values written to the response
将动态 C# 值写入响应的 HTML
❺ Standalone, static HTML
独立的静态 HTML

Razor Pages use a templating syntax called Razor that combines static HTML with dynamic C# code and HTML generation. The @page directive on the first line of the Razor Page is the most important. This directive must always be placed on the first line of the file, as it tells ASP.NET Core that the .cshtml file is a Razor Page. Without it, you won’t be able to view your page correctly.

Razor Pages 使用一种称为 Razor 的模板语法,该语法将静态 HTML 与动态 C# 代码和 HTML 生成相结合。Razor 页面第一行上的 @page 指令是最重要的。此指令必须始终放在文件的第一行,因为它会告知 ASP.NET Core .cshtml 文件是 Razor 页面。没有它,您将无法正确查看您的页面。

The next line of the Razor Page defines which PageModel in your project the Razor Page is associated with:

Razor Page 的下一行定义 Razor Page 与项目中的哪个 PageModel 相关联:

@model PrivacyModel

In this case the PageModel is called PrivacyModel, and it follows the standard convention for naming Razor Page models. You can find this class in the Privacy.cshtml.cs file in the Pages folder of your project, as shown in figure 13.5. Visual Studio nests these files underneath the Razor Page .cshtml files in Solution Explorer. We’ll look at the page model in section 13.1.4.

在这种情况下,PageModel 称为 PrivacyModel,它遵循命名 Razor Page 模型的标准约定。您可以在项目的 Pages 文件夹中的 Privacy.cshtml.cs 文件中找到此类,如图 13.5 所示。Visual Studio 将这些文件嵌套在 Razor 页面下.cshtml 文件。我们将在 13.1.4 节中查看页面模型。

alt text

Figure 13.5 By convention, page models for Razor Pages are placed in a file with the same name as the Razor Page, with a .cs suffix appended. Visual Studio nests these files below the Razor Page in Solution Explorer.
图 13.5 按照惯例,Razor 页面的页面模型放置在与 Razor 页面同名的文件中,并附加了 .cs 后缀。Visual Studio 将这些文件嵌套在“解决方案资源管理器”中的 Razor 页面下方。

In addition to the @page and @model directives, static HTML is always valid in a Razor Page and will be rendered as is in the response:

除了 @page 和 @model 指令之外,静态 HTML 在 Razor 页面中始终有效,并将按原样在响应中呈现:

<p>Use this page to detail your site’s privacy policy.</p>

You can also write ordinary C# code in Razor templates by using this construct:
您还可以使用以下构造在 Razor 模板中编写普通的 C# 代码:

@{ /* C# code here */ }

Any code between the curly braces will be executed but won’t be written to the response. In the listing, you’re setting the title of the page by writing a key to the ViewData dictionary, but you aren’t writing anything to the response at this point:

大括号之间的任何代码都将被执行,但不会写入响应。在清单中,您通过向 ViewData 字典写入一个键来设置页面的标题 ,但此时您没有向响应写入任何内容:

@{
    ViewData["Title"] = "Privacy Policy";
}

Another feature shown in this template is that you can dynamically write C# variables and expressions to the HTML stream using the @ symbol. This ability to combine dynamic and static markup is what gives Razor Pages their power. In the example, you’re fetching the "Title" value from the ViewData dictionary and writing the values to the response inside an

tag:

此模板中显示的另一个功能是,您可以使用 @ 符号将 C# 变量和表达式动态写入 HTML 流 。这种结合动态和静态标记的能力赋予了 Razor Pages 强大的功能。在此示例中,您将从 ViewData 字典中提取 “Title” 值,并将该值写入

标签内的响应:

<h1>@ViewData["Title"]</h1>

At this point, you might be a little confused by the template in listing 13.3 when it’s compared with the output shown in figure 13.3. The title and the static HTML content appear in both the listing and figure, but some parts of the final web page don’t appear in the template. How can that be?

此时,当与图 13.3 中所示的输出进行比较时,您可能会对清单 13.3 中的模板感到困惑。标题和静态 HTML 内容同时显示在列表和图中,但最终网页的某些部分不会显示在模板中。这怎么可能呢?

Razor Pages have the concept of layouts, which are base templates that define the common elements of your application, such as headers and footers. The HTML of the layout combines with the Razor Page template to produce the final HTML that’s sent to the browser. Layouts prevent you from having to duplicate code for the header and footer in every page, and mean that if you need to tweak something, you’ll need to do it in only one place.

Razor Pages 具有布局的概念,布局是定义应用程序的常见元素(如页眉和页脚)的基本模板。布局的 HTML 与 Razor Page 模板相结合,以生成发送到浏览器的最终 HTML。布局可以防止你为每个页面的页眉和页脚重复代码,这意味着如果你需要调整某些内容,只需在一个地方进行。

Note I cover Razor templates, including layouts, in detail in chapter 17. You can find layouts in the Pages/Shared folder of your project.
注意 我在第 17 章中详细介绍了 Razor 模板,包括布局。您可以在项目的 Pages/Shared 文件夹中找到布局。

As you’ve already seen, you can include C# code in your Razor Pages by using curly braces @{ }, but generally speaking, you’ll want to limit the code in your .cshtml file to presentational concerns only. Complex logic, code to access services such as a database, and data manipulation should be handled in the PageModel instead.

如你所见,你可以使用大括号 @{ } 在 Razor Pages 中包含 C# 代码,但一般来说,你需要将 .cshtml 文件中的代码限制为仅表示性问题。复杂的逻辑、访问服务(如数据库)的代码以及数据作应该在 PageModel 中处理。

13.1.4 Handling request logic with page models and handlers

13.1.4 使用页面模型和处理程序处理请求逻辑

As you’ve already seen, the @page directive in a .cshtml file marks the page as a Razor Page, but most Razor Pages also have an associated page model. By convention, this page model is placed in a file commonly known as a code-behind file that has a .cs extension, as you saw in figure 13.5. Page models should derive from the PageModel base class, and they typically contain one or more methods called page handlers that define how to handle requests to the Razor Page.

如你所见,.cshtml 文件中的 @page 指令将页面标记为 Razor 页面,但大多数 Razor 页面也有关联的页面模型。按照惯例,此页面模型被放置在一个通常称为代码隐藏文件的文件中,该文件具有 .cs 扩展名,如图 13.5 所示。页面模型应派生自 PageModel 基类,并且它们通常包含一个或多个称为 page 的方法定义如何处理对 Razor Page 的请求的处理程序。

Definition A page handler is the Razor Pages equivalent of a minimal API endpoint handler; it’s a method that runs in response to a request. Razor Page models must be derived from the PageModel class. They can contain multiple page handlers, though typically they contain only one or two.
定义 页面处理程序相当于最小 API 端点处理程序的 Razor Pages;它是为响应请求而运行的方法。Razor Page 模型必须派生自 PageModel 类。它们可以包含多个页面处理程序,但通常只包含一个或两个。

The following listing shows the page model for the Privacy.cshtml Razor Page, located in the file Privacy.cshtml.cs.

以下列表显示了位于文件 Privacy.cshtml.cs 中的 Privacy.cshtml Razor 页面的页面模型。

Listing 13.4 The PrivacyModel in Privacy.cshtml.cs: A Razor Page page model
清单 13.4 Privacy.cshtml.cs中的 PrivacyModel:一个Razor 页面模型

public class PrivacyModel: PageModel        ❶
{
    private readonly ILogger<PrivacyModel> _logger;      ❷ 
    public PrivacyModel(ILogger<PrivacyModel> logger)    ❷ 
    {                                                    ❷ 
        _logger = logger;                                ❷ 
    }                                                    ❷ 

  public void OnGet()      ❸ 
    {
    }
}

❶ Razor Pages must inherit from PageModel.
Razor Pages 必须继承自 PageModel。
❷ You can use dependency injection to provide services in the constructor.
您可以使用依赖注入在构造函数中提供服务。
❸ The default page handler is OnGet. Returning void indicates HTML should be generated.
默认页面处理程序为 OnGet。返回 void 表示应生成 HTML。

This page model is extremely simple, but it demonstrates a couple of important points:
此页面模型非常简单,但它演示了几个要点:

  • Page handlers are driven by convention.
    页面处理程序由约定驱动。

  • Page models can use dependency injection (DI) to interact with other services.
    页面模型可以使用依赖关系注入 (DI) 与其他服务进行交互。

Page handlers are typically named by convention, based on the HTTP verb they respond to. They return either void, indicating that the Razor
Page’s template should be rendered, or an IActionResult that contains other instructions for generating the response, such as redirecting the user to a different page.

页面处理程序通常根据它们响应的 HTTP 动词按约定命名。它们返回 void(指示应呈现 Razor 页面的模板)或 IActionResult(包含用于生成响应的其他说明,例如将用户重定向到其他页面)。

The PrivacyModel contains a single handler, OnGet, which indicates that it should run in response to GET requests for the page. As the method returns void, executing the handler executes the associated Razor template for the page to generate the HTML.

PrivacyModel 包含一个处理程序 OnGet,它指示它应该运行以响应页面的 GET 请求。当该方法返回 void 时,执行处理程序将执行页面的关联 Razor 模板以生成 HTML。

Note Razor Pages are focused on building page-based apps, so you typically want to return HTML rather than JSON or XML. You can also use an IActionResult to return any sort of data, to redirect users to a new page, or to send an error. You’ll learn more about IActionResults in chapter 15.
注意 Razor Pages 专注于构建基于页面的应用,因此你通常需要返回 HTML,而不是 JSON 或 XML。您还可以使用 IActionResult 返回任何类型的数据、将用户重定向到新页面或发送错误。您将在第 15 章中了解有关 IActionResult的更多信息。

DI is used to inject an ILogger instance into the constructor of the page model the same way you would inject a service into a minimal API endpoint handler. The service is unused in this example, but you’ll learn all about ILogger in chapter 26.

DI 用于将 ILogger 实例注入页面模型的构造函数中,就像将服务注入最小 API 终端节点处理程序中一样。在此示例中,该服务未使用,但您将在第 26 章中了解有关 ILogger 的所有信息。

Clearly, the PrivacyModel page model doesn’t do much in this case, and you may be wondering why it’s worth having. If all page models do is tell the Razor Page to generate HTML, why do we need them at all?

显然,在这种情况下,PrivacyModel 页面模型的作用不大,您可能想知道为什么值得拥有它。如果所有页面模型都只是告诉 Razor 页面生成 HTML,那么我们为什么还需要它们呢?

The key thing to remember here is that now you have a framework for performing arbitrarily complex functions in response to a request. You could easily update the handler method to load data from the database, send an email, add a product to a basket, or create an invoice—all in response to a simple HTTP request. This extensibility is where a lot of the power in Razor Pages (and the MVC pattern in general) lies.

这里要记住的关键是,现在你有一个框架来执行任意复杂的功能以响应请求。您可以轻松更新处理程序方法,以从数据库加载数据、发送电子邮件、将产品添加到购物篮或创建发票 - 所有这些都是为了响应简单的 HTTP 请求。这种可扩展性是 Razor Pages(以及一般的 MVC 模式)中许多功能所在。

The other important point is that you’ve separated the execution of these methods from the generation of the HTML. If the logic changes, and you need to add behavior to a page handler, you don’t need to touch the HTML generation code, so you’re less likely to introduce bugs. Conversely, if you need to change the UI slightly (change the color of the title, for example), your handler method logic is safe.

另一个重要的一点是,您已经将这些方法的执行与 HTML 的生成分开。如果逻辑发生变化,并且您需要向页面处理程序添加行为,则无需接触 HTML 生成代码,因此不太可能引入错误。相反,如果您需要稍微更改 UI(例如,更改标题的颜色),则处理程序方法逻辑是安全的。

And there you have it—a complete ASP.NET Core Razor Pages application! Before we move on, let’s take one last look at how your application handles a request. Figure 13.6 shows a request to the /Privacy path being handled by the sample application. You’ve seen everything here already, so the process of handling a request should be familiar. The figure shows how the request passes through the middleware pipeline before being handled by the endpoint middleware. The Privacy.cshtml Razor Page executes the OnGet handler and generates the HTML response, which passes back through the middleware to the ASP.NET Core web server before being sent to the user’s browser.

这就是一个完整的 ASP.NET Core Razor Pages 应用程序!在继续之前,让我们最后看看您的应用程序如何处理请求。图 13.6 显示了对示例应用程序正在处理的 /Privacy 路径的请求。您已经在这里看到了所有内容,因此处理请求的过程应该很熟悉。该图显示了请求在由终端节点中间件处理之前如何通过中间件管道。Privacy.cshtml Razor 页面执行 OnGet 处理程序并生成 HTML 响应,该响应传回通过中间件发送到 ASP.NET Core Web 服务器,然后再发送到用户的浏览器。

alt text

Figure 13.6 An overview of a request to the /Privacy URL for the sample ASP.NET Razor Pages application. The routing middleware routes the request to the OnGet handler of the Privacy.cshtml.cs Razor Page. The Razor Page generates an HTML response by executing the Razor template in Privacy.cshtml and passes the response back through the middleware pipeline to the browser.
图 13.6 对示例 ASP.NET Razor Pages 应用程序的 /Privacy URL 的请求概述。路由中间件将请求路由到 Privacy.cshtml.cs Razor Page 的 OnGet 处理程序。Razor 页面通过在 Privacy.cshtml 中执行 Razor 模板来生成 HTML 响应,并通过中间件管道将响应传回浏览器。

We’ve reached the end of this section working through the template, so you have a good overview of how an entire Razor Pages application is configured and how it handles a request using Razor Pages. In section 13.2 we take the basic Razor Pages in the default template a bit further, looking at a more complex example.

我们已完成本部分的末尾,逐步了解了模板的配置方式,以及它如何使用 Razor Pages 处理请求。在第 13.2 节中,我们将默认模板中的基本 Razor Pages 进一步介绍,并查看更复杂的示例。

13.2 Exploring a typical Razor Page

13.2 探索典型的 Razor 页面

The Razor Pages programming model was introduced in ASP.NET Core 2.0 as a way to build server-side rendered page-based websites. It builds on top of the ASP.NET Core infrastructure to provide a streamlined experience, using conventions where possible to reduce the amount of boilerplate code and configuration required. In this section we’ll look at a more complex page model to better understand the overall design of Razor Pages.

Razor Pages 编程模型是在 ASP.NET Core 2.0 中引入的,作为构建服务器端呈现的基于页面的网站的一种方式。它建立在 ASP.NET Core 基础设施之上,以提供简化的体验,并尽可能使用约定来减少所需的样板代码和配置的数量。本节内容我们将查看更复杂的页面模型,以更好地了解 Razor Pages 的整体设计。

In listing 13.4 you saw a simple Razor Page that didn’t contain any logic; instead, it only rendered the associated Razor view. This pattern may be common if you’re building a content-heavy marketing website, for example, but more commonly your Razor Pages will contain some logic, load data from a database, or use forms to allow users to submit information.

在清单 13.4 中,你看到了一个简单的 Razor 页面,它不包含任何逻辑;相反,它只呈现关联的 Razor 视图。例如,如果您正在构建内容繁重的营销网站,则此模式可能很常见,但更常见的是,您的 Razor 页面将包含一些逻辑、从数据库加载数据或使用表单来允许用户提交信息。

To give you more of a flavor of how typical Razor Pages work, in this section we look briefly at a slightly more complex Razor Page. This page is taken from a to-do list application and is used to display all the to-do items for a given category. We’re not focusing on the HTML generation at this point, so the following listing shows only the PageModel code-behind file for the Razor Page.

为了让您更深入地了解典型的 Razor 页面的工作原理,在本节中,我们将简要介绍一个稍微复杂一些的 Razor 页面。此页面取自待办事项列表应用程序,用于显示给定类别的所有待办事项。目前,我们不关注 HTML 生成,因此下面的清单仅显示 Razor Page 的 PageModel 代码隐藏文件。

Listing 13.5 A Razor Page for viewing all to-do items in a given category
清单 13.5 用于查看所有待办事项的 Razor 页面在给定类别中

public class CategoryModel : PageModel
{
    private readonly ToDoService _service;       ❶
    public CategoryModel(ToDoService service)    ❶
    {
        _service = service;
    }

    public ActionResult OnGet(string category)    ❷
    {
        Items = _service.GetItemsForCategory(category);     ❸
        return Page();     ❹
    }

    public List<ToDoListModel> Items { get; set; }    ❺
}

❶ The ToDoService is provided in the model constructor using DI.
ToDoService 是使用 DI 在模型构造函数中提供的。
❷ The OnGet handler takes a parameter, category.
OnGet 处理程序采用参数 category。
❸ The handler calls out to the ToDoService to retrieve data and sets the Items property.
处理程序调用 ToDoService 以检索数据并设置 Items 属性。
❹ Returns a PageResult indicating the Razor view should be rendered
返回一个 PageResult,指示应呈现 Razor 视图
❺ The Razor View can access the Items property when it’s rendered.
Razor 视图在呈现时可以访问 Items 属性。

This example is still relatively simple, but it demonstrates a variety of features compared with the basic example from listing 13.4:
这个例子仍然相对简单,但与 清单 13.4 中的基本例子相比,它演示了各种特性:

  • The page handler, OnGet, accepts a method parameter, category. This parameter is automatically populated using values from the incoming request via model binding, similar to the way binding works with minimal APIs. I discuss Razor Pages model binding in detail in chapter 16.
    页面处理程序 OnGet 接受方法参数 category。此参数通过模型绑定使用传入请求中的值自动填充,类似于绑定使用最小 API 的方式。我在第 16 章中详细讨论了 Razor Pages 模型绑定。

  • The handler doesn’t interact with the database directly. Instead, it uses the category value provided to interact with the ToDoService, which is injected as a constructor argument using DI.
    处理程序不直接与数据库交互。相反,它使用提供的 category 值与 ToDoService 交互,后者使用 DI 作为构造函数参数注入。

  • The handler returns Page() at the end of the method to indicate that the associated Razor view should be rendered. The return statement is optional in this case; by convention, if the page handler is a void method, the Razor view will still be rendered, behaving as though you called return Page() at the end of the method.
    处理程序在方法末尾返回 Page(),以指示应呈现关联的 Razor 视图。在这种情况下,return 语句是可选的;按照约定,如果页面处理程序是 void 方法,则仍将呈现 Razor 视图,其行为就像在方法末尾调用 return Page() 一样。

  • The Razor View has access to the CategoryModel instance, so it can access the Items property that’s set by the handler. It uses these items to build the HTML that is ultimately sent to the user.
    Razor 视图有权访问 CategoryModel 实例,因此它可以访问处理程序设置的 Items 属性。它使用这些项来构建最终发送给用户的 HTML。‌

The pattern of interactions in the Razor Page of listing 13.5 shows a common pattern. The page handler is the central controller for the Razor Page. It receives an input from the user (the category method parameter); calls out to the “brains” of the application (the ToDoService); and passes data (by exposing the Items property) to the Razor view, which generates the HTML response. If you squint, this pattern looks like the MVC design pattern.

清单 13.5 的 Razor Page 中的交互模式显示了一种常见模式。页面处理程序是 Razor Page 的中央控制器。它接收来自用户的输入(category 方法参数);调用应用程序的“大脑”(ToDoService);并将数据(通过公开 Items 属性)传递到 Razor 视图,后者会生成 HTML 响应。如果您眯着眼睛看,此模式看起来类似于 MVC 设计模式。

Depending on your background in software development, you may have come across the MVC pattern in some form. In web development, MVC is a common paradigm, used in frameworks such as Django, Rails, and Spring MVC. But as it’s such a broad concept, you can find MVC in everything from mobile apps to rich-client desktop applications. I hope that indicates the benefits of the pattern when it’s used correctly! In section 13.3 we’ll look at the MVC pattern in general and how ASP.NET Core uses it.

根据你的软件开发背景,你可能遇到过某种形式的 MVC 模式。在 Web 开发中,MVC 是一种常见的范式,用于 Django、Rails 和 Spring MVC 等框架。但是,由于 MVC 是一个如此广泛的概念,因此您可以在从移动应用程序到富客户端桌面应用程序的所有应用程序中找到 MVC。我希望这表明正确使用该模式的好处!在 Section 13.3 中,我们将大致了解 MVC 模式以及 ASP.NET Core 如何使用它。

13.3 Understanding the MVC design pattern

13.3 了解 MVC 设计模式

The MVC design pattern is a common pattern for designing apps that have UIs. The MVC pattern has many interpretations, each of which focuses on a slightly different aspect of the pattern. The original MVC design pattern was specified with rich-client graphical user interface (GUI) apps in mind, rather than web applications, so it uses terminology and paradigms associated with a GUI environment. Fundamentally, though, the pattern aims to separate the management and manipulation of data from its visual representation.

MVC 设计模式是设计具有 UI 的应用程序的常见模式。MVC 模式有多种解释,每种解释都侧重于模式的一个略有不同的方面。最初的 MVC 设计模式在指定时考虑了富客户端图形用户界面 (GUI) 应用程序,而不是 Web 应用程序,因此它使用了与 GUI 环境相关的术语和范例。不过,从根本上说,该模式旨在将数据的管理和作与其视觉表示分开。

Before I dive too far into the design pattern itself, let’s consider a typical Razor Pages request. Imagine that a user requests the Razor Page from listing 13.5 that displays a to-do list category. Figure 13.7 shows how a Razor Page handles different aspects of a request, all of which combine to generate the final response.

在深入研究设计模式本身之前,让我们考虑一个典型的 Razor Pages 请求。假设用户从显示待办事项列表类别的清单 13.5 中请求 Razor 页面。图 13.7 显示了 Razor Page 如何处理请求的不同方面,所有这些方面结合起来生成最终响应。

alt text

Figure 13.7 Requesting a to-do list page for a Razor Pages application. A different component handles each aspect of the request.
图 13.7 请求 Razor Pages 应用程序的待办事项列表页面。不同的组件处理请求的每个方面。

In general, three components make up the MVC design pattern:

通常,MVC 设计模式由三个组件组成:

  • Model—The data that needs to be displayed—the global state of the application. It’s accessed via the ToDoService in listing 13.5.
    Model (模型) - 需要显示的数据 - 应用程序的全局状态。它可以通过清单 13.5 中的 ToDoService 访问。

  • View—The template that displays the data provided by the model.
    View (视图) - 显示模型提供的数据的模板。

  • Controller—Updates the model and provides the data for display to the view. This role is taken by the page handler in Razor Pages—the OnGet method in listing 13.5.
    Controller (控制器) - 更新模型并提供数据以显示在视图中。此角色由 Razor Pages 中的页面处理程序(列表 13.5 中的 OnGet 方法)担任。

Each component of the MVC design pattern is responsible for a single aspect of the overall system, which, when combined, generates a UI. The to-do list example considers MVC in terms of a web application using Razor Pages, but a generalized request could be equivalent to the click of a button in a desktop GUI application.

MVC 设计模式的每个组件都负责整个系统的一个方面,当这些方面组合在一起时,将生成一个 UI。待办事项列表示例从使用 Razor Pages 的 Web 应用程序的角度考虑 MVC,但通用请求可能等效于单击桌面 GUI 应用程序中的按钮。

In general, the order of events when an application responds to a user interaction or request is as follows:

通常,应用程序响应用户交互或请求时的事件顺序如下:

  1. The controller (the Razor Page handler) receives the request.
    控制器 (Razor Page 处理程序) 接收请求。

  2. Depending on the request, the controller either fetches the requested data from the application model using injected services or updates the data that makes up the model.
    根据请求,控制器要么使用注入的服务从应用程序模型中获取请求的数据,要么更新构成模型的数据。

  3. The controller selects a view to display and passes a representation of the model (the view model) to it.
    控制器选择要显示的视图,并将模型的表示 (视图模型) 传递给该视图。

  4. The view uses the data contained in the model to generate the UI.
    该视图使用模型中包含的数据来生成 UI。

When we describe MVC in this format, the controller (the Razor Page handler) serves as the entry point for the interaction. The user communicates with the controller to instigate an interaction. In web applications, this interaction takes the form of an HTTP request, so when a request to a URL is received, the controller handles it.

当我们以这种格式描述 MVC 时,控制器(Razor Page 处理程序)将用作交互的入口点。用户与控制器通信以引发交互。在 Web 应用程序中,此交互采用 HTTP 请求的形式,因此当收到对 URL 的请求时,控制器会对其进行处理。

Depending on the nature of the request, the controller may take a variety of actions, but the key point is that the actions are undertaken using the application model. The model here contains all the business logic for the application, so it’s able to provide requested data or perform actions.

根据请求的性质,控制器可能会采取各种作,但关键是这些作是使用应用程序模型执行的。此处的模型包含应用程序的所有业务逻辑,因此它能够提供请求的数据或执行作。

Note In this description of MVC, the model is considered to be a complex beast, containing all the logic for how to perform an action, as well as any internal state. The Razor Page PageModel class is not the model we’re talking about! Unfortunately, as in all software development, naming things is hard.
注意 在 MVC 的这个描述中,模型被认为是一个复杂的野兽,包含如何执行作的所有逻辑,以及任何内部状态。Razor Page PageModel 类不是我们正在谈论的模型!不幸的是,就像在所有软件开发中一样,命名事物是困难的。

Consider a request to view a product page for an e-commerce application. The controller would receive the request and know how to contact some product service that’s part of the application model. This service might fetch the details of the requested product from a database and return them to the controller.

考虑查看电子商务应用程序的产品页面的请求。控制器将收到请求并知道如何联系属于应用程序模型的某些产品服务。此服务可能会从数据库中获取所请求产品的详细信息,并将其返回给控制器。

Alternatively, imagine that a controller receives a request to add a product to the user’s shopping cart. The controller would receive the request and most likely would invoke a method on the model to request that the product be added. Then the model would update its internal representation of the user’s cart, by adding (for example) a new row to a database table holding the user’s data.

或者,假设控制器收到将产品添加到用户购物车的请求。控制器将收到请求,并且很可能会调用模型上的方法以请求添加产品。然后,该模型将更新其用户购物车的内部表示,方法是向包含用户数据的数据库表添加新行(例如)。

Tip You can think of each Razor Page handler as being a mini controller focused on a single page. Every web request is another independent call to a controller that orchestrates the response. Although there are many controllers, all the handlers interact with the same application model.
提示 您可以将每个 Razor Page 处理程序视为一个专注于单个页面的迷你控制器。每个 Web 请求都是对编排响应的控制器的另一个独立调用。尽管有许多控制器,但所有处理程序都与相同的应用程序模型交互。

After the model has been updated, the controller needs to decide what response to generate. One of the advantages of using the MVC design pattern is that the model representing the application’s data is decoupled from the final representation of that data, called the view. The controller is responsible for deciding whether the response should generate an HTML view, whether it should send the user to a new page, or whether it should return an error page.

更新模型后,控制器需要决定要生成什么响应。使用 MVC 设计模式的一个优点是,表示应用程序数据的模型与该数据的最终表示形式(称为视图)分离。控制器负责决定响应是否应生成 HTML 视图,是否应将用户发送到新页面,或者是否应返回错误页面。

One of the advantages of the model’s being independent of the view is that it improves testability. UI code is classically hard to test, as it’s dependent on the environment; anyone who has written UI tests simulating a user clicking buttons and typing in forms knows that it’s typically fragile. By keeping the model independent of the view, you can ensure that the model stays easily testable, without any dependencies on UI constructs. As the model often contains your application’s business logic, this is clearly a good thing!

模型独立于视图的优点之一是它提高了可测试性。UI 代码通常很难测试,因为它依赖于环境;任何编写过模拟用户单击按钮和在表单中键入的 UI 测试的人都知道,它通常是脆弱的。通过使模型独立于视图,您可以确保模型易于测试,而不依赖于 UI 构造。由于该模型通常包含应用程序的业务逻辑,这显然是一件好事!

The view can use the data passed to it by the controller to generate the appropriate HTML response. The view is responsible only for generating the final representation of the data; it’s not involved in any of the business logic.

视图可以使用控制器传递给它的数据来生成适当的 HTML 响应。视图仅负责生成数据的最终表示形式;它不涉及任何业务逻辑。

This is all there is to the MVC design pattern in relation to web applications. Much of the confusion related to MVC seems to stem from slightly different uses of the term for slightly different frameworks and types of applications. In section 13.4 I’ll show how the ASP.NET Core framework uses the MVC pattern with Razor Pages, along with more examples of the pattern in action.

这就是 MVC 设计模式与 Web 应用程序相关的全部内容。许多与 MVC 相关的混淆似乎源于该术语对框架和应用程序类型的用法略有不同。在第 13.4 节中,我将展示 ASP.NET Core 框架如何将 MVC 模式与 Razor Pages 结合使用,以及该模式的更多实际示例。

13.4 Applying the MVC design pattern to Razor Pages

13.4 将 MVC 设计模式应用于 Razor Pages

In section 13.3 I discussed the MVC pattern as it’s typically used in web applications; Razor Pages use this pattern. But ASP.NET Core also includes a framework called ASP.NET Core MVC. This framework (unsurprisingly) closely mirrors the MVC design pattern, using controllers containing action methods in place of Razor Pages and page handlers. Razor Pages builds directly on top of the underlying ASP.NET Core MVC framework, using the MVC framework under the hood for their behavior.

在 Section 13.3 中,我讨论了 MVC 模式,因为它通常用于 Web 应用程序;Razor Pages 使用此模式。但 ASP.NET Core 还包括一个名为 ASP.NET Core MVC 的框架。此框架(不出所料)与 MVC 设计模式紧密镜像,使用包含作方法的控制器来代替 Razor Pages 和页面处理程序。Razor Pages 直接构建在基础 ASP.NET Core MVC 框架之上,并在后台使用 MVC 框架来实现其行为。

If you prefer, you can avoid Razor Pages and work with the MVC framework directly in ASP.NET Core. This option was the only one in early versions of ASP.NET Core and the previous version of ASP.NET.

如果愿意,可以避免使用 Razor Pages,直接在 ASP.NET Core 中使用 MVC 框架。此选项是 ASP.NET Core 早期版本和 ASP.NET 早期版本中的唯一选项。

Tip I look in greater depth at choosing between Razor Pages and the MVC framework in chapter 19.
提示 在第 19 章中,我将更深入地了解如何在 Razor Pages 和 MVC 框架之间进行选择。

In this section we look in greater depth at how the MVC design pattern applies to Razor Pages in ASP.NET Core. This section will also help clarify the role of various features of Razor Pages.

在本部分中,我们将更深入地了解 MVC 设计模式如何应用于 ASP.NET Core 中的 Razor 页面。这部分还将有助于阐明 Razor Pages 的各种功能的作用。

Do Razor Pages use MVC or MVVM?
Razor Pages 使用 MVC 还是 MVVM?
Occasionally, I’ve seen people describe Razor Pages as using the Model-View-View Model (MVVM) design pattern rather than the MVC design pattern. don’t agree, but it’s worth being aware of the differences.
有时,我看到人们将 Razor Pages 描述为使用模型-视图-视图模型 (MVVM) 设计模式,而不是 MVC 设计模式。我不同意,但值得意识到差异。
MVVM is a UI pattern that is often used in mobile apps, desktop apps, and some client-side frameworks. It differs from MVC in that there is a bidirectional interaction between the view and the view model. The view model tells the view what to display, but the view can also trigger changes directly on the view model. It’s often used with two-way data binding where a view model is bound to a view.
MVVM 是一种 UI 模式,通常用于移动应用程序、桌面应用程序和一些客户端框架。它与 MVC 的不同之处在于,视图和视图模型之间存在双向交互。视图模型告诉视图要显示什么,但视图也可以直接在视图模型上触发更改。它通常与双向数据绑定一起使用,其中视图模型绑定到视图。
Some people consider the Razor Pages PageModel to be filling this role, but I’m not convinced. Razor Pages definitely seems based on the MVC pattern to me (it’s based on the ASP.NET Core MVC framework after all!), and it doesn’t have the same two-way binding that I would expect with MVVM.
有些人认为 Razor Pages PageModel 正在扮演这个角色,但我不相信。在我看来,Razor Pages 绝对是基于 MVC 模式的(毕竟它基于 ASP.NET Core MVC 框架!),并且它没有我所期望的 MVVM 的双向绑定。

As you’ve seen in previous chapters, ASP.NET Core implements Razor Page endpoints using a combination of RoutingMiddleware and EndpointMiddleware, as shown in figure 13.8. When a request has been processed by earlier middleware (and assuming that none has handled the request and short-circuited the pipeline), the routing middleware selects which Razor Page handler should be executed, and the endpoint middleware executes the page handler.
如前几章所示,ASP.NET Core 使用 RoutingMiddleware 和 EndpointMiddleware 的组合实现 Razor Page 终结点,如图 13.8 所示。当请求由早期中间件处理时(并假设没有中间件处理请求并使管道短路),路由中间件将选择应执行哪个 Razor Page 处理程序,而终结点中间件将执行页面处理程序。

alt text

Figure 13.8 The middleware pipeline for a typical ASP.NET Core application. The request is processed by middleware in sequence. If the request reaches the routing middleware, the middleware selects an endpoint, such as a Razor Page, to execute. The endpoint middleware executes the selected endpoint.
图 13.8 典型 ASP.NET Core 应用程序的中间件管道。中间件会按顺序处理该请求。如果请求到达路由中间件,中间件会选择一个端点(例如 Razor Page)来执行。端点中间件执行选定的端点。

As you’ve seen in earlier chapters, middleware often handles cross-cutting concerns or narrowly defined requests, such as requests for files. For requirements that fall outside these functions or that have many external dependencies, a more robust framework is required. Razor Pages (and/or ASP.NET Core MVC) can provide this framework, allowing interaction with your application’s core business logic and the generation of a UI. It handles everything from mapping the request to an appropriate page handler (or controller action method) to generating the HTML response.

正如你在前面的章节中所看到的,中间件通常处理横切关注点或狭义定义的请求,比如文件请求。对于超出这些功能或具有许多外部依赖项的需求,需要更健壮的框架。Razor Pages(和/或 ASP.NET Core MVC)可以提供此框架,允许与应用程序的核心业务逻辑交互并生成 UI。它处理从将请求映射到适当的页面处理程序(或控制器作方法)到生成 HTML 响应的所有作。

In the traditional description of the MVC design pattern, there’s only a single type of model, which holds all the non-UI data and behavior. The controller updates this model as appropriate and then passes it to the view, which uses it to generate a UI.

在 MVC 设计模式的传统描述中,只有一种类型的模型,它包含所有非 UI 数据和行为。控制器会根据需要更新此模型,然后将其传递给视图,视图会使用它来生成 UI。

One of the problems when discussing MVC is the vague and ambiguous terms that it uses, such as controller and model. Model in particular is such an overloaded term that it’s often difficult to be sure exactly what it refers to; is it an object, a collection of objects, or an abstract concept? Even ASP.NET Core uses the word model to describe several related but different components, as you’ll see later in this chapter.

讨论 MVC 时的问题之一是它使用的术语模糊不清,例如 controller 和 model。特别是 Model 是一个超载的术语,以至于通常很难确定它到底指的是什么;它是一个对象、对象的集合还是一个抽象概念?Even ASP.NET Core 也使用单词 model 来描述几个相关但不同的组件,您将在本章后面看到。

13.4.1 Directing a request to a Razor Page and building a binding model

13.4.1 将请求定向到 Razor 页面并构建绑定模型

The first step when your app receives a request is routing the request to an appropriate Razor Page handler in the routing middleware. Let’s think again about the category to-do list page in listing 13.5. On that page, you’re displaying a list of items that have a given category label. If you’re looking at the list of items with a category of Simple, you’d make a request to the /category/Simple path.

应用收到请求时的第一步是将请求路由到路由中间件中的相应 Razor Page 处理程序。让我们再考虑一下清单 13.5 中的类别 to- do 列表页面。在该页面上,将显示具有给定类别标签的项目列表。如果您正在查看类别为 Simple 的项目列表,则需要向 /category/Simple 路径发出请求。

Routing maps a request URL, /category/Simple, against the route patterns registered with your application. You’ve seen how this process works for minimal APIs, and it’s the same for Razor Pages; each route template corresponds to a Razor Page endpoint. You’ll learn more about routing with Razor Pages in chapter 14.

路由将请求 URL /category/Simple 映射到应用程序中注册的路由模式。您已经了解了此过程如何适用于最小 API,对于 Razor Pages 也是如此;每个路由模板对应于一个 Razor Page 终结点。您将在第 14 章中了解有关使用 Razor Pages 进行路由的更多信息。

Tip I’m using the term Razor Page to refer to the combination of the Razor view and the PageModel that includes the page handler. Note that that PageModel class is not the model we’re referring to when describing the MVC pattern. It fulfills other roles, as you’ll see later in this section.
提示 我使用术语 Razor Page 来指代 Razor 视图和包含页面处理程序的 PageModel 的组合。请注意,PageModel 类不是我们在描述 MVC 模式时所指的模型。它履行其他角色,如本节后面所述。

When a page handler is selected in the routing middleware, the request continues down the middleware pipeline until it reaches the endpoint middleware, where the Razor Page executes.

在路由中间件中选择页面处理程序后,请求将继续沿中间件管道向下,直到到达执行 Razor Page 的终结点中间件。

First, the binding model (if applicable) is generated. This model is built from the incoming request, based on the properties of the PageModel marked for binding and the method parameters required by the page handler, as shown in figure 13.9. A binding model is normally one or more standard C# objects and works similarly to the way it works in minimal APIs, as you saw in chapter 6. We’ll look at Razor Page binding models in detail in chapter 16.

首先,生成绑定模型(如果适用)。此模型是根据传入请求构建的,基于标记为绑定的 PageModel 属性和页面处理程序所需的方法参数,如图 13.9 所示。绑定模型通常是一个或多个标准 C# 对象,其工作方式类似于它在最小 API 中的工作方式,如第 6 章所示。我们将在第 16 章中详细介绍 Razor Page 绑定模型。

alt text

Figure 13.9 Routing a request to a Razor Page and building a binding model. A request to the /category/Simple URL results in the execution of the CategoryModel.OnGet page handler, passing in a populated binding model, category.
图 13.9 将请求路由到 Razor 页面并构建绑定模型。对/category/Simple URL 会导致执行 CategoryModel.OnGet 页面处理程序,并传入填充的绑定模型 category。

Definition A binding model is one or more objects that act as a container for the data provided in a request—data that’s required by a page handler.
定义 绑定模型是一个或多个对象,它们充当请求中提供的数据的容器,这些数据是由页面处理程序要求。

In this case, the binding model is a simple string, category, that’s bound to the "Simple" value. This value is provided in the request URL’s path. A more complex binding model could have been used, with multiple properties populated with values from the route template, the query string, and the request body.

在本例中,绑定模型是绑定到 “Simple” 值的简单字符串 category。此值在请求 URL 的路径中提供。可以使用更复杂的绑定模型,其中多个属性填充了路由模板、查询字符串和请求正文中的值。

Note The binding model for Razor Pages is conceptually equivalent to all the parameters you pass in to a minimal API endpoint that are populated from the request.
注意 Razor Pages 的绑定模型在概念上等效于您传递给从请求填充的最小 API 端点的所有参数。

The binding model in this case corresponds to the method parameter of the OnGet page handler. An instance of the Razor Page is created using its constructor, and the binding model is passed to the page handler when it executes, so it can be used to decide how to respond. For this example, the page handler uses it to decide which to-do items to display on the page.

在这种情况下,绑定模型对应于 OnGet 页面处理程序的 method 参数。Razor Page 的实例是使用其构造函数创建的,绑定模型在执行时传递给页面处理程序,因此可用于决定如何响应。在此示例中,页面处理程序使用它来决定要在页面上显示哪些待办事项。

13.4.2 Executing a handler using the application model

13.4.2 使用应用程序模型执行处理程序

The role of the page handler as the controller in the MVC pattern is to coordinate the generation of a response to the request it’s handling. That means it should perform only a limited number of actions. In particular, it should

在 MVC 模式中,页面处理程序作为控制器的角色是协调对它正在处理的请求的响应的生成。这意味着它应该只执行有限数量的作。特别是,它应该

  • Validate that the data contained in the binding model is valid for the request.
    验证绑定模型中包含的数据是否对请求有效。

  • Invoke the appropriate actions on the application model using services.
    使用 services 对应用程序模型调用适当的作。

  • Select an appropriate response to generate based on the response from the application model.
    根据应用程序模型的响应选择要生成的适当响应。

Figure 13.10 shows the page handler invoking an appropriate method on the application model. Here, you can see that the application model is a somewhat-abstract concept that encapsulates the remaining non-UI parts of your application. It contains the domain model, several services, and the database interaction.

图 13.10 显示了在应用程序模型上调用适当方法的页面处理程序。在这里,您可以看到应用程序模型是一个有点抽象的概念,它封装了应用程序的其余非 UI 部分。它包含域模型、多个服务和数据库交互。

alt text

Figure 13.10 When executed, an action invokes the appropriate methods in the application model.
图 13.10 执行时,作会调用应用程序模型中的相应方法。

Definition The domain model encapsulates complex business logic in a series of classes that don’t depend on any infrastructure and are easy to test.
定义 域模型将复杂的业务逻辑封装在一系列不依赖于任何基础设施且易于测试的类中。

The page handler typically calls into a single point in the application model. In our example of viewing a to-do list category, the application model might use a variety of services to check whether the current user is allowed to view certain items, to search for items in the given category, to load the details from the database, or to load a picture associated with an item from a file. Assuming that the request is valid, the application model returns the required details to the page handler. Then it’s up to the page handler to choose a response to generate.

页面处理程序通常调用应用程序模型中的单个点。在查看待办事项列表类别的示例中,应用程序模型可能使用各种服务来检查是否允许当前用户查看某些项目、搜索给定类别中的项目、从数据库加载详细信息或从文件中加载与项目关联的图片。假设请求有效,应用程序模型会将所需的详细信息返回给页面处理程序。然后,由页面处理程序选择要生成的响应。

13.4.3 Building HTML using the view model

13.4.3 使用视图模型构建 HTML

When the page handler has called out to the application model that contains the application business logic, it’s time to generate a response. A view model captures the details necessary for the view to generate a response.

当页面处理程序调用了包含应用程序业务逻辑的应用程序模型时,就可以生成响应了。视图模型捕获视图生成响应所需的详细信息。

Definition A view model in the MVC pattern is all the data required by the view to render a UI. It’s typically some transformation of the data contained in the application model, plus extra information required to render the page, such as the page’s title.
定义 MVC 模式中的视图模型是视图呈现 UI 所需的所有数据。它通常是对应用程序模型中包含的数据进行一些转换,以及呈现页面所需的额外信息,例如页面的标题。

The term view model is used extensively in ASP.NET Core MVC, where it typically refers to a single object that is passed to the Razor view to render. With Razor Pages, however, the Razor view can access the Razor Page’s page model class directly. Therefore, the Razor Page PageModel typically acts as the view model in Razor Pages, with the data required by the Razor view exposed via properties, as you saw in listing 13.5.

术语“视图模型”在 ASP.NET Core MVC 中广泛使用,它通常是指传递给 Razor 视图进行渲染的单个对象。但是,使用 Razor 页面时,Razor 视图可以直接访问 Razor 页面的页面模型类。因此,Razor Page PageModel 通常充当 Razor Pages 中的视图模型,其中通过 properties 公开的 Razor 视图所需的数据,如清单 13.5 所示。

Note Razor Pages use the PageModel class itself as the view model for the Razor view by exposing the required data as properties.
注意 Razor Pages 通过将所需数据作为属性公开,使用 PageModel 类本身作为 Razor 视图的视图模型。

The Razor view uses the data exposed in the page model to generate the final HTML response. Finally, this data is sent back through the middleware pipeline and out to the user’s browser, as shown in figure 13.11.

Razor 视图使用页面模型中公开的数据来生成最终的 HTML 响应。最后,这些数据通过中间件管道发送回用户的浏览器,如图 13.11 所示。

alt text

Figure 13.11 The page handler builds a view model by setting properties on the PageModel. It’s the view that generates the response.
图 13.11 页面处理程序通过在 PageModel 上设置属性来构建视图模型。这是生成响应。

It’s important to note that although the page handler selects whether to execute the view and the data to use, it doesn’t control what HTML is generated. The view itself decides what the content of the response will be.

请务必注意,尽管页面处理程序选择是否执行视图和要使用的数据,但它并不控制生成的 HTML。视图本身决定响应的内容。

13.4.4 Putting it all together: A complete Razor Page request

13.4.4 汇总:完整的 Razor Page 请求

Now that you’ve seen the steps that go into handling a request in ASP.NET Core using Razor Pages, let’s put them together from request to response. Figure 13.12 shows how the steps combine to handle the request to display the list of to-do items for the Simple category. The traditional MVC pattern is still visible in Razor Pages, made up of the page handler (controller), the view, and the application model.

现在,你已经了解了使用 Razor Pages 在 ASP.NET Core 中处理请求的步骤,让我们将它们从请求到响应放在一起。图 13.12 显示了如何组合这些步骤来处理显示 Simple 类别的待办事项列表的请求。传统的 MVC 模式在 Razor Pages 中仍然可见,它由页面处理程序(控制器)、视图和应用程序模型组成。

alt text

Figure 13.12 A complete Razor Pages request for the list of to-dos in the Simple category
图 13.12 对“简单”类别中待办事项列表的完整 Razor Pages 请求

By now, you may be thinking this whole process seems rather convoluted. So many steps to display some HTML! Why not allow the application model to create the view directly, rather than have to go on a dance back and forth with the page handler method? The key benefit throughout this process is the separation of concerns:

到现在为止,您可能会认为整个过程似乎相当复杂。显示一些 HTML 的步骤太多了!为什么不允许应用程序模型直接创建视图,而不必使用页面处理程序方法来回跳舞呢?整个过程中的主要好处是关注点分离:

  • The view is responsible only for taking some data and generating HTML.
    该视图只负责获取一些数据并生成 HTML。

  • The application model is responsible only for executing the required business logic.
    应用程序模型仅负责执行所需的业务逻辑。

  • The page handler (controller) is responsible only for validating the incoming request and selecting which response is required, based on the output of the application model.
    页面处理程序(控制器)仅负责验证传入请求并根据应用程序模型的输出选择所需的响应。

By having clearly defined boundaries, it’s easier to update and test each of the components without depending on any of the others. If your UI logic changes, you won’t necessarily have to modify any of your business logic classes, so you’re less likely to introduce errors in unexpected places.
通过明确定义的边界,可以更轻松地更新和测试每个组件,而无需依赖任何其他组件。如果您的 UI 逻辑发生变化,您不一定必须修改任何业务逻辑类,因此您不太可能在意想不到的地方引入错误。

The dangers of tight coupling
紧密耦合的危险
It’s generally a good idea to reduce coupling between logically separate parts of your application as much as possible. This makes it easier to update your application without causing adverse effects or requiring modifications in seemingly unrelated areas. Applying the MVC pattern is one way to help with this goal.
通常,最好尽可能减少应用程序逻辑上独立的部分之间的耦合。这样可以更轻松地更新您的应用程序,而不会造成不利影响或需要在看似不相关的区域中进行修改。应用 MVC 模式是帮助实现此目标的一种方法。

As an example of when coupling rears its head, I remember a case a few years ago when I was working on a small web app. In our haste, we hadn’t decoupled our business logic from our HTML generation code properly, but initially there were no obvious problems. The code worked, so we shipped it!

举个例子,我记得几年前我正在开发一个小型 Web 应用程序。由于匆忙,我们没有将业务逻辑与 HTML 生成代码正确解耦,但最初并没有明显的问题。代码有效,所以我们发布了它!

A few months later, someone new started working on the app and immediately “helped” by renaming an innocuous spelling error in a class in the business layer. Unfortunately, the names of those classes had been used to generate our HTML code, so renaming the class caused the whole website to break in users’ browsers! Suffice it to say that we made a concerted effort to apply the MVC pattern thereafter and ensure that we had a proper separation of concerns.

几个月后,有人开始开发该应用程序,并立即“帮助”重命名了业务层中一个无害的类中的一个无害的拼写错误。不幸的是,这些类的名称已被用于生成我们的 HTML 代码,因此重命名该类会导致整个网站在用户的浏览器中中断!可以说,我们在此后齐心协力地应用 MVC 模式,并确保我们适当地分离了关注点。

The examples shown in this chapter demonstrate the bulk of the Razor Pages functionality. It has additional features, such as the filter pipeline, which I cover in chapters 21 and 22, and I discuss binding models in greater depth in chapter 16, but the overall behavior of the system is the same.

本章中显示的示例演示了 Razor Pages 的大部分功能。它还有其他功能,例如过滤器管道,我在第 21 章和第 22 章中介绍了这一点,我在第 16 章中更深入地讨论了绑定模型,但系统的整体行为是相同的。

Similarly, in chapter 19 I look at MVC controllers and explain why I don’t recommend them over Razor Pages for server-rendered applications. By contrast, in chapter 20 I discuss how you can use the MVC design pattern when you’re generating machine-readable responses using Web API controllers. The process is for all intents and purposes identical to the MVC pattern you’ve already seen.

同样,在第 19 章中,我将介绍 MVC 控制器,并解释为什么对于服务器呈现的应用程序,我不建议使用 MVC 控制器而不是 Razor Pages。相比之下,在第 20 章中,我将讨论在使用 Web API 控制器生成机器可读响应时如何使用 MVC 设计模式。该过程的所有意图和目的都与您已经看到的 MVC 模式相同。

I hope that by this point, you’re sold on Razor Pages and their overall design using the MVC pattern. The page handler methods on a Razor Page are invoked in response to a request and select the type of response to generate by returning an IActionResult.

我希望到此时,您已经对使用 MVC 模式的 Razor Pages 及其整体设计感到满意。调用 Razor 页面上的页面处理程序方法以响应请求,并通过返回 IActionResult 选择要生成的响应类型。

An aspect I’ve touched on only vaguely is how the RoutingMiddleware decides which Razor Page and handler to invoke for a given request. You don’t want to have a Razor Page for every URL in an app. It would be difficult to have, for example, a different page per product in an e-shop; every product would need its own Razor Page! In chapter 14 you’ll see how to define routes for your Razor Pages, how to add constraints to your routes, and how they deconstruct URLs to match a single handler.

我只是模糊地谈到了一个方面,即 RoutingMiddleware 如何决定为给定请求调用哪个 Razor Page 和处理程序。您不希望应用程序中的每个 URL 都有一个 Razor 页面。例如,在电子商店中为每个产品设置不同的页面是很困难的;每个产品都需要自己的 Razor Page!在第 14 章中,你将了解如何为 Razor 页面定义路由,如何向路由添加约束,以及它们如何解构 URL 以匹配单个处理程序。

13.5 Summary

13.5 总结

  • Razor Pages are located in the Pages folder of a project and by default are named according to the URL path they handle. Privacy.cshtml, for example, handles the path /Privacy. This convention makes it easy to quickly add new pages.
    Razor Pages 位于项目的 Pages 文件夹中,默认情况下根据它们处理的 URL 路径进行命名。例如,Privacy.cshtml 处理路径 /Privacy。此约定使快速添加新页面变得容易。

  • Razor Pages must contain the @page directive as the first line of the .cshtml file. Without this directive, ASP.NET Core won’t recognize it as a Razor Page, and it won’t appear as an endpoint in your app.
    Razor Pages 必须包含 @page 指令作为 .cshtml 文件的第一行。如果没有此指令,ASP.NET Core 将无法将其识别为 Razor 页面,并且不会在应用中显示为终结点。

  • Page models derive from the PageModel base class and contain page handlers. Page handlers are methods named using conventions that indicate the HTTP verb they handle. OnGet, for example, handles the GET verb. Page handlers are equivalent to minimal API endpoint handlers; they run in response to a given request.
    页面模型派生自 PageModel 基类,并包含页面处理程序。页面处理程序是使用约定命名的方法,这些约定指示它们处理的 HTTP 动词。例如,OnGet 处理 GET 动词。页面处理程序等同于最小 API 端点处理程序;它们运行以响应给定的请求。

  • Razor templates can contain standalone C#, standalone HTML, and dynamic HTML generated from C# values. By combining all three, you can build highly dynamic applications.
    Razor 模板可以包含独立 C#、独立 HTML 和从 C# 值生成的动态 HTML。通过将这三者结合起来,您可以构建高度动态的应用程序。

  • The MVC design pattern allows for a separation of concerns between the business logic of your application, the data that’s passed around, and the display of data in a response. This reduces coupling between the different layers of your application.
    MVC 设计模式允许在应用程序的业务逻辑、传递的数据和响应中的数据显示之间分离关注点。这减少了应用程序不同层之间的耦合。

  • Razor Pages should inherit from the PageModel base class and contain page handlers. The routing middleware selects a page handler based on the incoming request’s URL, the HTTP verb, and the request’s query string.
    Razor Pages 应继承自 PageModel 基类并包含页面处理程序。路由中间件根据传入请求的 URL、HTTP 动词和请求的查询字符串选择页面处理程序。

  • Page handlers generally should delegate to services to handle the business logic required by a request instead of performing the changes themselves. This ensures a clean separation of concerns that aids testing and improves application structure.
    页面处理程序通常应委托给服务来处理请求所需的业务逻辑,而不是自行执行更改。这确保了关注点的清晰分离,从而有助于测试并改进应用程序结构。

Leave a Reply

Your email address will not be published. Required fields are marked *