本章要点 (请点击这里阅读其他章节)
- 创建第一个 ASP.NET Core Web 应用程序
- 运行应用程序
- 了解应用程序的组件
读完第1章后,您应该对 ASP.NET Core 应用程序的工作原理以及何时使用它们有了大致的了解。您还应该设置了一个开发环境,可以用来开始构建应用程序。
提示:有关安装 .NET 5.0 SDK 和选择编辑器 /IDE 的指南,请参阅附录 A。
在本章中,您将通过创建第一个 Web 应用程序来深入了解并感受一下它是如何工作的。在后面的章节中,我将向你展示如何定制和构建自己的应用程序。
在完成本章的工作时,您应该开始掌握构成 ASP.NET Core 应用程序的各种组件,并了解一般应用程序构建过程。您创建的大多数应用程序都将从类似的模板开始,因此尽快熟悉相关设置过程是一个不错的主意。
定义:模板提供构建应用程序所需的基本代码。您可以使用模板作为构建自己应用程序的起点。
我将首先向您展示如何使用 Visual Studio 模板之一创建基本 ASP.NET Core 应用程序。如果您使用的是其他工具,如 .NET CLI,则可以使用类似的模板。在本章中,我将 Visual Studio 2019 和 ASP.NET Core 5.0 与 .NET 5.0 一起使用,但我也提供了使用 .NET CLI 的提示。
提示:您可以在本书的 GitHub 存储库中查看本章的应用程序代码,网址为 https://github.com/andrewlock/asp-dot-net-core-in-action-2e。
创建应用程序后,我将向您展示如何恢复所有必要的依赖关系、编译应用程序并运行它以查看 HTML 输出。该应用程序在某些方面很简单,它只有两个不同的页面,但它将是一个完全配置的 ASP.NET Core 应用程序。
运行完应用程序后,下一步将是了解发生了什么!我们将学习 ASP.NET Core 应用程序的所有主要部分,了解如何配置 Web 服务器、中间件管道和 HTML 生成等。在这个阶段我们将不详细介绍,但您将了解它们是如何协同工作以创建完整的应用程序的。
我们将从开始一个新项目时创建的大量文件开始,您将了解典型的 ASP.NET Core 应用程序是如何布局的。特别是,我将重点介绍 Program.cs 和 Startup.cs 文件。实际上,应用程序的整个配置都发生在这两个文件中,因此最好了解它们的用途和使用方式。您将看到如何为应用程序定义中间件管道,以及如何自定义它。
最后,您将看到应用程序如何响应请求生成 HTML,并查看构成 Razor Pages 端点的每个组件。您将看到它如何控制响应请求运行的代码,以及如何定义应为特定请求返回的 HTML。
在这个阶段,如果你发现项目的某些部分令人困惑或复杂,不要担心;在阅读本书时,您将详细探究每一部分。在本章结束时,您应该基本了解 ASP.NET Core 应用程序是如何组合在一起的,从应用程序首次运行到生成响应。在开始之前,我们将回顾 ASP.NET Core 应用程序如何处理请求。
2.1 ASP.NET Core 应用程序的简要概述
在第1章中,我描述了浏览器如何向服务器发出 HTTP 请求并接收响应,该响应用于在页面上呈现 HTML。ASP.NET Core 允许您根据请求的详细信息动态生成 HTML,例如,您可以根据当前登录的用户显示不同的数据。
假设您想创建一个 Web 应用程序来显示有关您公司的信息。您可以创建一个简单的 ASP.NET Cor e应用程序来实现这一点;稍后,您可以向应用程序添加动态功能。图2.1显示了应用程序如何处理程序中的页面请求。
从第1章的图1.8中,您应该对该图的大部分内容很熟悉;请求和响应、反向代理和 ASP.NET Core Web 服务器都仍然存在,但您会注意到我已经扩展了 ASP.NET Core 应用程序本身,以显示中间件管道和端点中间件。这是应用程序中用于生成请求响应的主要自定义部分。
反向代理转发请求后的第一个调用端口是 ASP.NET Core Web 服务器,这是默认的跨平台 Kestrel 服务器。Kestrel 接收原始传入网络请求,并使用它生成一个 HttpContext 对象,应用程序的其余部分可以使用该对象。
HttpContext 对象
ASP.NET Core Web 服务器构建的 HttpContext 被应用程序用作单个请求的存储盒。此特定请求和后续响应的任何内容都可以与之关联并存储在其中。这可能包括请求的属性、特定于请求的服务、已加载的数据或发生的错误。Web 服务器用原始 HTTP 请求的详细信息和其他配置详细信息填充初始 HttpContext,并将其传递给应用程序的其余部分。
注:Kestrel 不是 ASP.NET Core 中唯一可用的 HTTP 服务器,但它是性能最高的,而且是跨平台的。我只会在全书中提到 Kestrel。主要替代方案 HTTP.sys 仅在 Windows 上运行,不能与 IIS 一起使用。
Kestrel 负责接收请求数据并构造请求的 C# 表示,但它不尝试直接生成响应。为此,Kestrel 将 HttpContext 交给 ASP.NET Core 应用程序中的中间件管道。这是一系列组件,用于处理传入请求以执行常见操作,如日志记录、处理异常或提供静态文件。
注意:您将在下一章详细了解中间件管道。
中间件管道的末端是端点中间件。这个中间件负责调用生成最终响应的代码。在大多数应用程序中,将是 MVC 或 Razor Pages 块。
Razor Pages 负责生成构成典型 ASP.NET Core Web 应用程序页面的 HTML。它们通常也是应用程序的大部分业务逻辑所在,调用各种服务以响应原始请求中包含的数据。并非每个应用程序都需要 MVC 或 Razor Pages 块,但这通常是构建大多数向用户显示HTML的应用程序的方式。
注:我将在第4章中介绍 Razor Pages 和 MVC 控制器,包括如何在它们之间进行选择。我在第7章和第8章中介绍了生成 HTML。
大多数 ASP.NET Core 应用程序都遵循这个基本架构,本章中的示例也没有什么不同。首先,您将看到如何创建和运行应用程序,然后我们将查看代码如何与图2.1中的大纲相对应。不用再多说,让我们创建一个应用程序!
2.2 创建第一个 ASP.NET Core 应用程序
您可以根据所使用的工具和操作系统,以多种不同的方式开始使用 ASP.NET Core 构建应用程序。每一组工具都有稍微不同的模板,但它们有很多相似之处。本章中使用的示例基于 Visual Studio 2019 模板,但您可以很容易地使用 .NET CLI 或 Visual Studio for Mac 中的模板。
提醒:本章使用 Visual Studio 2019 和 ASP.NET Core 5.0 以及 .NET 5.0。
启动和运行应用程序通常涉及四个基本步骤,我们将在本章中介绍这些步骤:
- 生成——从模板创建基础应用程序以开始。
- 还原——使用NuGet将所有包和依赖项还原到本地项目文件夹。
- 编译——应用程序并生成所有必要的中间代码。
- 运行——运行编译的应用程序。
Visual Studio 和 .NET CLI 包含许多 ASP.NET Core 模板,用于构建不同类型的应用程序。例如:
- RazorPages Web 应用程序 —— RazorPage 应用程序在服务器上生成 HTML,并设计为用户可以直接在 Web 浏览器中查看。
- MVC(模型-视图-控制器)应用程序 —— MVC 应用程序与 RazorPages 应用程序相似,因为它们在服务器上生成 HTML,并设计为用户可以直接在 Web 浏览器中查看。他们使用传统的 MVC 控制器而不是 RazorPages。
- Web API 应用程序 —— Web API 应用以可供单页应用程序(SPA)和 API 使用的格式返回数据。它们通常与 Angular 和 React.js 等客户端应用程序或移动应用程序结合使用。
我们将在本书中介绍每种应用程序类型,但在本章中,我们将重点介绍 RazorPages 模板。
2.2.1 使用模板开始
使用模板可以快速启动并运行应用程序,自动配置许多基本部分。Visual Studio 和 .NET CLI 都附带了许多用于构建 Web 应用程序、控制台应用程序和类库的标准模板。
提示:在 .NET 中,项目是一个部署单元,它将被编译为 .dll 文件或可执行文件。每个单独的应用程序都是一个单独的项目。一个解决方案中可以同时构建和开发多个项目。
要创建第一个 Web 应用程序,请打开 Visual Studio 并执行以下步骤:
1. 从启动屏幕中选择“创建新项目”,或从 Visual Studio 主屏幕中选择文件>新建>项目。
2. 从模板列表中,选择 ASP.NET Core Web Application,确保选择 C# 语言模板,如图2.2所示。单击 Next。
图2.2 新项目对话框。从右侧的列表中选择 C# ASP.NET Core Web应用程序模板。下次创建新项目时,可以从左侧的最近模板列表中进行选择。
3. 在下一个屏幕上,输入项目名称、位置和解决方案名称,然后单击 Create,如图2.3所示。例如,使用 WebApplication1 作为项目和解决方案的名称。
图2.3 “配置新项目”对话框。要创建新的 .NET 5.0 应用程序,请从模板屏幕中选择 ASP.NET Core Web 应用程序。在下面的屏幕上,输入项目名称、位置和解决方案名称,然后单击“创建”。
4. 在以下屏幕上(图2.4):
– 确保已选择 .NET Core。
– 选择 ASP.NET Core 5.0。如果此选项不可用,请确保已安装 .NET 5.0。有关配置环境的详细信息,请参阅附录 A。
– 选择 ASP.NET Core Web App 以创建 RazorPages Web 应用程序。
– 确保未指定身份验证。在第14章中,您将学习如何将用户添加到应用程序。
– 确保选中了 Configure for HTTPS。
– 确保未选中“启用 Docker 支持”。
– 单击“创建”。
图2.4 Web 应用程序模板屏幕。此屏幕位于“配置您的项目”对话框之后,允许您自定义将生成应用程序的模板。对于这个初学者项目,您将创建一个没有身份验证的 RazorPages Web 应用程序。
5. 等待 Visual Studio 从模板生成应用程序。Visual Studio 完成后,您将看到一个关于 ASP.NET Core 的介绍页面,您应该能够看到 Visual Studio 已经创建并向您的项目添加了许多文件,如图2.5所示。
图2.5 从模板创建新 ASP.NET Core 应用程序后的 Visual Studio。解决方案资源管理器显示新创建的项目。介绍页面提供了有关 ASP.NET Core 的有用链接。
如果未使用 Visual Studio,则可以使用 .NET CLI 创建类似的模板。创建文件夹以保存新项目。在文件夹(在 Windows 上)或终端会话(在 Linux 或 macOS上)中打开 PowerShell 或 cmd 提示符,然后运行以下列表中的命令。
清单2.1 使用 .NET CLI 创建新的 RazorPage 应用程序
dotnet new sln -n WebApplication1 //Create a solution file called WebApplication1 in the current folder. dotnet new Webapp -o WebApplication1 //Create a RazorPages project in a subfolder, WebApplication1. dotnet sln add WebApplication1 //Add the new project to the solution file.
无论您使用 Visual Studio 还是 .NET CLI,现在都已具备构建和运行第一个 ASP.NET Core 应用程序所需的基本文件。
2.2.2 构建应用程序
此时,您已经拥有运行应用程序所需的大部分文件,但还剩下两个步骤。首先,您需要确保将项目使用的所有依赖项复制到本地目录,其次,您需要编译应用程序,以便它可以运行。
这些步骤中的第一步并不是绝对必要的,因为 Visual Studio 和 .NET CLI 在第一次创建项目时都会自动还原包,但最好了解情况。在2.0之前的 .NET CLI 早期版本中,您需要使用 dotnet 手动还原包。
您可以通过选择 Build>Build Solution、使用快捷键 Ctrl-Shift-B 或从命令行运行 dotnet Build 来编译应用程序。如果您从 Visual Studio 构建,输出窗口将显示构建的进度,并且假设一切都很好,将编译您的应用程序,准备运行。
您还可以从 Visual Studio 中的包管理器控制台运行 dotnet 构建控制台命令。
提示:如果 Visual Studio 和 .NET CLI 工具检测到文件已更改,则在您运行应用程序时,它们将自动构建应用程序,因此您通常不需要自己明确执行此步骤。
NuGet 包和 .NET 命令行界面
.NET 5.0 跨平台开发的基本组件之一是 .NET 命令行界面(CLI)。这为创建、构建和运行 .NET 5.0 应用程序提供了几个基本命令。VisualStudio 有效地自动调用这些,但如果使用不同的编辑器,也可以直接从命令行调用它们。开发过程中最常用的命令是
dotnet restore
dotnet build
dotnet run
这些命令中的每一个都应在项目文件夹中运行,并将单独对该项目执行。
大多数 ASP.NET Core 应用程序依赖于各种外部库,这些库通过NuGet包管理器进行管理。项目中列出了这些依赖项,但不包括库本身的文件。在构建和运行应用程序之前,需要确保项目文件夹中有每个依赖项的本地副本。第一个命令 dotnet restore 确保将应用程序的 NuGet 依赖项复制到项目文件夹中。
ASP.NET Core 项目在项目的 .csproj 文件中列出其依赖项。这是一个 XML 文件,将每个依赖项作为 PackageReference 节点列出。当您运行 dotnet 还原时,它使用此文件来确定要下载哪些 NuGe t包并将其复制到项目文件夹。列出的任何依赖项都可以在应用程序中使用。
还原过程通常在构建或运行应用程序时隐式发生,但有时在连续集成构建管道中显式运行它会很有用。
您可以使用 dotnet build 编译应用程序。这将检查应用程序中的任何错误,如果没有问题,将生成可以使用 dotnet 运行的输出。
每个命令都包含许多可以修改其行为的开关。要查看可用命令的完整列表,请运行 dotnet --help
或查看特定命令的可用选项,例如,运行 dotnet new --help
2.3 运行 Web 应用程序
您已经准备好运行第一个应用程序,有许多不同的方法可以实现。在 Visual Studio 中,您可以单击 IIS Express 旁边工具栏上的绿色箭头,也可以按 F5 快捷键。Visual Studio 将使用适当的URL自动为您打开一个 Web 浏览器窗口,一两秒钟后,您将看到全新的应用程序,如图2.6所示。或者,您可以使用 dotnet run 从命令行使用 .NET CLI 工具运行应用程序,并在 Web 浏览器中手动打开 URL,使用命令行上提供的地址。
图2.6 新 ASP.NET Core 应用程序的主页。当您从 Visual Studio 运行它时,默认情况下,IIS Express 会选择一个随机端口。如果您从命令行运行 dotnet run,您的应用程序将在 http://localhost:5000和https://localhost:5001.
提示:首次从 Visual Studio 运行应用程序时,将提示您安装开发证书。这样做可以确保您的浏览器不会显示有关无效证书的警告。有关 HTTPS 证书的更多信息,请参阅第18章。(您可以在 Windows 和 macOS 上安装开发证书。有关在 Linux 上信任证书的说明,请参阅发行版的说明。并非所有浏览器(例如 Mozilla Firefox)都使用证书存储,因此请遵循浏览器信任证书的指南。如果您仍有困难,请参阅 http://mng.bz/1rmy。)
默认情况下,此页面显示一个简单的欢迎横幅和指向 ASP.NET Core 官方 Microsoft 文档的链接。页面顶部有两个链接:Home 和 Privacy。Home 链接是您当前所在的页面。单击 Privacy 将转到一个新页面,如图2.7所示。不久您将看到,您可以在应用程序中使用 RazorPages 来定义这两个页面并构建它们显示的 HTML。
图2.7 应用程序的 Privacy 页面。您可以使用应用程序标题中的 Home 和 Privacy 链接在应用程序的两个页面之间导航。该应用程序使用 RazorPages 生成页面内容。
在这一点上,你需要注意一些事情。首先,包含链接和应用程序标题“WebApplication1”的标题在两个页面上是相同的。第二,页面的标题(如浏览器的选项卡中所示)将更改以匹配当前页面。在第7章中,当我们讨论使用 Razor 模板对 HTML 进行排序时,您将看到如何实现这些功能。
注:您只能在当前运行该应用程序的同一台计算机上查看该应用程序;您的应用程序尚未暴露在互联网上。您将在第16章中学习如何发布和部署应用程序。
在这个阶段,应用程序的用户体验没有任何变化。单击一下,一旦你对应用程序的行为感到满意,卷起袖子,是时候看看一些代码了!
2.4 了解项目布局
当您刚接触框架时,从这样的模板创建应用程序可能是一件喜忧参半的事情。一方面,您可以快速启动并运行应用程序,而无需输入任何信息。相反,文件的数量有时会让人应接不暇,让你绞尽脑汁想从哪里开始。基本的 Web 应用程序模板不包含大量的文件和文件夹,如图2.8所示,但我将详细介绍主要的模板,以使您了解方向。
图2.8 新 ASP.NET Core 应用程序的解决方案浏览器和磁盘上的文件夹。解决方案资源管理器还显示“连接的服务”和“依赖项”节点,其中列出了 NuGet 和其他依赖项,尽管磁盘上不存在文件夹本身。
首先要注意的是,主项目 WebApplication1 嵌套在具有解决方案名称的顶层目录中,在本例中也是 WebApplication1。在这个顶级文件夹中,您还可以找到 Visual Studio 使用的解决方案(.sln)文件以及与 Git 版本控制相关的文件,尽管这些文件隐藏在 Visual Studio 的解决方案资源管理器视图中。
注意:Visual Studio 使用解决方案的概念来处理多个项目。示例解决方案仅由 .sln 文件中列出的单个项目组成。如果使用 CLI 模板创建项目,则不会有 .sln 或 Git 文件,除非使用其他 .NET CLI 模板显式生成它们。
在解决方案文件夹中,您将找到项目文件夹,该文件夹依次包含三个子文件夹 Pages、Propertie s和 wwwroot。Pages(毫不奇怪)包含用于构建应用程序的 RazorPages 文件。Properties 文件夹包含一个文件 launchSettings.json,它控制 Visual Studio 如何运行和调试应用程序。wwwroot 文件夹很特殊,因为它是应用程序中唯一允许浏览器在浏览 Web 应用程序时直接访问的文件夹。您可以将 CSS、JavaScript、图像或静态 HTML 文件存储在此处,浏览器将能够访问它们。他们将无法访问 wwwroot 之外的任何文件。
虽然 wwwroot 和 Properties 文件夹存在于磁盘上,但您可以看到 Solution Explore r将它们显示为特殊节点,按字母顺序排列,位于项目顶部附近。项目中还有两个特殊节点,即 Dependencies 和 Connected Services,但它们在磁盘上没有相应的文件夹。相反,它们显示了项目所依赖的所有依赖项的集合,例如 NuGet 包和远程服务。
在项目文件夹的根目录中,您将找到两个 JSON 文件:appsettings.json 和 appsettings.Development.json。这些提供了在运行时用于控制应用程序行为的配置设置。
项目中最重要的文件是 WebApplication1.csproj,因为它描述了如何构建项目。Visual Studio 没有显式显示 .csproj 文件,但如果在解决方案资源管理器中双击项目名称,则可以对其进行编辑。我们将在下一节中详细了解这个项目文件。
最后,Visual Studio 在项目文件夹 Program.c 和 Startup.cs 中显示了两个 C# 文件。在第2.6节和第2.7节中,您将看到这些基本类如何负责配置和运行应用程序。
2.5 .csproj 项目文件:定义依赖项
.csproj 文件是 .NET 应用程序的项目文件,包含构建.NET项目所需的详细信息。它定义了正在构建的项目类型(Web 应用程序、控制台应用程序或库)、项目目标平台(.NET Core 3.1、.NET 5.0 等)以及项目所依赖的 NuGet 包。
该项目文件一直是.NET应用程序的支柱,但在 ASP.NET Core 中,它进行了一次改版,使其更易于阅读和编辑。这些变化包括
- No GUID —— 以前,全局唯一标识符(GUID)用于许多事情,但现在很少在项目文件中使用。
- Implicit file includes —— 以前,项目中的每个文件都必须列在 .csproj 文件中,才能包含在构建中。现在,文件被自动编译。
- No paths to NuGet package .dll files —— 以前,您必须在 .csproj 中包含 NuGet 包中包含的.dll文件的路径,并在包中列出依赖项和配置文件。现在,您可以直接在 .csproj 中引用 NuGet 包,而不需要指定磁盘上的路径。
所有这些更改结合在一起,使项目文件比以前的 .NET 项目更加紧凑。下面的列表显示了示例应用程序的整个 .csproj 文件。
清单2.2 .cspro j项目文件,显示 SDK、目标框架和引用
<Project Sdk="Microsoft.NET.Sdk.Web"> //SDK属性指定您正在构建的项目类型。 <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> </Project> //TargetFramework是您将运行的框架,在本例中为.NET 5.0.
对于简单的应用程序,您可能不需要太多地更改项目文件。Project 元素上的Sdk属性包括描述如何构建项目的默认设置,而 TargetFramework 元素描述应用程序将在其上运行的框架。对于 .NET Core 3.1 项目,这将具有 netcore-app3.1 值;如果您在 .NET 5.0 上运行,则这将是 net5.0。
提示:Visual Studio 用户可以在解决方案资源管理器中双击项目以编辑 .csproj 文件,而无需先关闭项目。
您对项目文件进行的最常见更改是使用 PackageReference 元素添加其他 NuGet 包。默认情况下,您的应用程序根本不引用任何 NuGet 包。
在项目中使用 NuGet 库
尽管所有应用程序在某些方面都是独一无二的,但它们也有共同的需求。例如,大多数应用程序需要访问数据库或为格式化数据处理 JSON 或 XML。您应该使用现有的可重用库,而不是在每个项目中重新创建代码。
NuGet 是 .NET 的库包管理器,其中库被打包到 NuGet 包中并发布到 https://nuget.org。通过引用 .csproj 文件中的唯一包名,可以在项目中使用这些包。这些使包的命名空间和类在代码文件中可用。您可以将 NuGet 包发布(并托管)到存储库,而不是 https://nuget.org —— 参见 https://docs.microsoft.com/nuget 详细信息。
通过在项目文件夹中运行 dotnet add package<packagename>,可以将 NuGet 引用添加到项目中。这将使用<PackageReference>节点更新项目文件,并恢复项目的 NuGet 包。例如,安装流行的 Newtonsoft.Json 库,你会运行
dotnet add package Newtonsoft.Json
这将向项目文件中添加对库的最新版本的引用,如下图所示,并使 Newtonsoft.Json 命名空间在源代码文件中可用。
<Project Sdk=“Microsoft.NET.Sdk.Web”>
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include=“NewtonSoft.Json”Version=“12.0.3”/>
</ItemGroup>
<Project>
如果您使用的是 Visual Studio,则可以通过右键单击解决方案名称或项目并选择“管理 NuGet 包”来使用 NuGet 包管理器管理包。
有趣的是,NuGet 的发音没有得到官方认可。欢迎使用流行的“noo-get”或“nugget”,或者如果你觉得特别时髦,“noo-jay”!
与以前的版本相比,简化的项目文件格式更易于手动编辑,如果您是跨平台开发,这是非常好的。但如果您使用的是 Visual Studio,则不必走这条路。您仍然可以使用 GUI 添加项目引用、排除文件、管理NuGet包等。Visual Studio 将一如既往地更新项目文件本身。
提示:有关 .csproj 格式更改的更多详细信息,请参阅 http://mng.bz/PPGg。
项目文件定义了 Visual Studio 和 .NET CLI 构建应用程序所需的一切。当然,除了代码!在下一节中,我们将查看 ASP.NET Core 应用程序的入口点 Program.cs 类。
2.6 Program 类:构建 Web 主机
所有 ASP.NET Core 应用程序的启动方式与 .NET 控制台应用程序相同 —— 使用 Program.cs 文件。该文件包含一个静态 void Main 函数,这是控制台应用程序的标准特性。此方法必须存在,并在启动Web应用程序时调用。
提示:.NET 5.0 和 C#9 引入了“顶级语句”,它隐式地创建了 Main 入口点。我在本书中没有使用顶级语句,但 ASP.NET Core 5.0 支持它们。有关详细信息,请参阅文档:http://mng.bz/JDaP。
在 ASP.NET Core 应用程序中,Main 入口点用于构建和运行 IHost 实例,如以下列表所示,其中显示了默认的 Program.cs 文件。IHost 是 ASP.NET Core 应用程序的核心,包含应用程序配置和侦听请求并发送响应的 Kestrel 服务器。
清单2.3 默认 Program.cs 文件配置并运行 IWebHost
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args) //使用CreateHostBuilder方法创建IHostBuilder。
.Build() //从IHostBuilder生成并返回IHost的实例。
.Run(); //运行IHost并开始侦听请求并生成
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args) //使用默认配置创建IHostBuilder。
.ConfigureWebHostDefaults(webBuilder => //配置应用程序以使用Kestrel并侦听HTTP请求。
{
webBuilder.UseStartup<Startup>(); //Startup类定义了应用程序的大部分配置。
};
}
}
Main 函数包含创建 Web 服务器和开始侦听请求所需的所有基本初始化代码。它通过调用 CreateDefaultBuilder 创建的 IHostBuilder 来定义如何配置 IHost,然后通过调用 Build() 来实例化 IHost。
注意:您会发现这种使用构建器对象来配置复杂对象的模式在 ASP.NET Core 框架中重复出现。这是一种有用的技术,允许用户配置对象,将其创建延迟到所有配置完成。这是 Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides(Addison Wesley,1994)在“Gang of Four”一书《Design Patterns: Elements of Reusable Object-Oriented Software》中描述的模式之一。
应用程序的大部分配置都是在调用 CreateDefaultBuilder 创建的 IHostBuilder 中进行的,但它将一些责任委托给了一个单独的类 Startup。通用 UseStartup<>方法中引用的 Startup 类是配置应用程序服务和定义中间件管道的地方。在第2.7节中,我们将花一段时间深入研究这一关键课程。
此时,您可能想知道为什么需要两个类进行配置:Program 和 Startup。为什么不在一个类包含应用程序的所有配置?
图2.9显示了 Program 和 Startup 之间配置组件的典型划分。一般来说,Program 是配置应用程序基础结构的地方,例如 HTTP 服务器、与 IIS 的集成以及配置源。相反,Startup 是定义应用程序使用的组件和功能以及应用程序的中间件管道的地方。
图2.9 Program 和 Startup 配置范围的差异。Program 关注的是在整个项目生命周期内通常保持稳定的基础设施配置。相反,您通常会修改 Startup 以添加新功能并更新应用程序行为。
两个不同 ASP.NET Core 应用程序的 Program 类通常是相似的,但 Startup 类通常会有很大的不同(尽管它们通常遵循相似的模式,稍后您将看到)。您很少会发现,随着应用程序的增长,您需要修改 Program,而您通常会在添加其他功能时更新 Startup。例如,如果您在项目中添加了新的 NuGet 依赖项,通常需要更新 Startup 以利用它。
Program 类是进行大量应用程序配置的地方,但在默认模板中,这隐藏在 CreateDefaultBuilder 方法中。CreateDefaultBuilder 是一个静态帮助器方法,它通过使用一些常见配置创建 IHostBuilder 来简化应用程序的引导。在第11章中,我们将深入了解此方法并探索配置系统,但现在只需记住图2.9,并注意如果需要,您可以完全更改 IHost 配置。
默认情况下使用的另一个 Helper 方法是 ConfigureWebHostDefaults。这使用 WebHostBuilder 对象来配置 Kestrel 以侦听 HTTP 请求。
使用通用主机创建服务
您必须同时调用 ConfigureWebHostDefaults 和 CreateDefaultBuilder,这可能看起来很奇怪。难道我们只调用一个方法吗?处理 HTTP 请求不是 ASP.NET Core 的唯一目的?
好吧,是的,也不是!ASP.NET Core 3.0 引入了通用主机的概念。这允许您使用与 ASP.NET Core 应用程序相同的框架来编写非 HTTP 应用程序。例如,这些应用程序可以作为控制台应用程序运行,也可以作为 Windows 服务(或作为 Linux 上的 systemd 守护程序)安装,以运行后台任务或从消息队列读取。
Kestrel 和 ASP.NET Core 的 Web 框架建立在 ASP.NET Core 3.0 中引入的通用主机功能之上。要配置典型的 ASP.NET Core 应用程序,您需要配置所有应用程序通用的通用主机特性;如:配置、日志记录和依赖服务等功能。对于 Web 应用程序,您还需要配置处理 Web 请求所需的服务,如 Kestrel。
在第22章中,您将看到如何使用通用主机构建应用程序以运行任务和构建服务。
一旦 IHostBuilder 的配置完成,对 Build 的调用将生成 IHost 实例,但应用程序仍然没有处理 HTTP 请求。调用 Run 启动 HTTP 服务器侦听。此时,您的应用程序已完全运行,可以响应来自远程浏览器的第一个请求。
2.7 Startup 类:配置应用程序
正如您所看到的,Program 负责为您的应用程序配置许多基础设施,但您需要在 Startup中 配置一些应用程序的行为。Startup 类负责配置应用程序的两个主要方面:
- 服务注册 —— 所有应用程序依赖的类(包括提供框架使用的功能和特定于应用程序的功能)都必须注册,以便在运行时正确实例化。
- 中间件和端点 —— 应用程序如何处理和响应请求。
您可以在 Startup 中使用自己的方法配置每个方面:ConfigureServices 中的服务注册,configure 中的中间件配置。启动的典型概要如下所示。
清单2.4 Startup.cs 的概要,显示如何配置每个方面
public class Startup
{
public void ConfigureServices(IServiceCollection services) //通过向IServiceCollection注册服务来配置服务。
{
// method details
}
public void Configure(IApplicationBuilder app) //配置用于处理HTTP请求的中间件管道。
{
// method details
}
}
Program 中创建的 IHostBuilder 调用 ConfigureServices,然后调用 Configure,如图2.10所示。每个调用分别配置应用程序的不同部分,使其可用于后续方法调用。ConfigureServices 方法中注册的所有服务都可用于 Configure方法。配置完成后,通过调用 IHostBuilder 上的 Build() 来创建 IHost。
图2.10 IHostBuilder 在 Program.cs 中创建,并在启动时调用方法来配置应用程序的服务和中间件管道。配置完成后,通过调用 IHostBuilder 上的 Build()来创建 IHost。
Startup 类的一个有趣之处是它没有实现这样的接口。相反,通过使用反射来调用这些方法,以查找具有预定义名称 Configure 和 ConfigureServices 的方法。这使类更加灵活,并允许您修改方法的签名以接受自动实现的其他参数。我将在第10章中详细介绍这是如何工作的;现在,只要知道 ConfigureServices 中配置的任何内容都可以通过 Configure 方法访问就足够了。
定义:.NET 中的反射允许您在运行时获取有关应用程序中类型的信息。您可以使用反射在运行时创建类的实例,并调用和访问它们。
因为 Startup 类是 ASP.NET Core 应用程序的基础,所以第2.7节将指导您完成 ConfigureServices 和 Configure,让您了解如何使用它们。我不会详细解释它们(详细介绍将会在本书的其他部分!),但是您应该记住它们是如何相互继承的,以及它们对整个应用程序配置的贡献。
2.7.1 添加和配置服务
ASP.NET Core 为每个不同的功能使用小型模块化组件。这允许单独的功能相对独立,只与其他功能松散耦合,通常认为这是良好的设计实现。这种方法的缺点是,它给功能的使用者带来了正确实例化功能的负担。在应用程序中,这些模块化组件作为应用程序使用的一个或多个服务公开。
定义:在 ASP.NET Core 的上下文中,服务是指为应用程序提供功能的所有类。这些可能是您为应用程序编写的库或代码所公开的类。
例如,在电子商务应用程序中,您可能有一个 TaxCalculator,它可以计算特定产品的应纳税额,同时考虑用户在世界上的不同位置所带来的费用影响,您可能有一个 ShippingCostService,它计算运送到用户所在地的成本。第三个服务 OrderTotalCalculatorService 可能使用这两个服务来计算用户必须为订单支付的总价。每个服务都提供一小部分独立的功能,但您可以将它们组合起来创建一个完整的应用程序。这就是所谓的单一责任原则。
定义:单一责任原则(SRP)规定,每个类只应负责一部分功能,只有在所需功能发生变化时才需要更改。这是 Robert C.Martin 在《Agile Software Development, Principles, Patterns, and Practices》(Pearson,2013)中提出的五项主要设计原则之一。
OrderTotalCalculatorService 需要访问 ShippingCostService 和 TaxCalculator 的实例。解决这个问题的一种简单方法是使用新关键字,并在需要时创建服务实例。不幸的是,这将您的代码与您正在使用的特定实现紧密结合,并可能会完全取消通过模块化功能实现的所有良好工作。在某些情况下,执行您创建的服务的初始化代码也可能破坏 SRP。
解决这个问题的一个方法是在编写服务时,可以声明依赖项,并让另一个类为您填充这些依赖项。然后,您的服务可以专注于它所设计的功能,而不是试图找出如何构建其依赖关系。
这种技术被称为依赖注入或控制反转(IoC)原理,是一种被广泛使用的公认的设计模式。
定义:设计模式是常见软件设计问题的解决方案。
通常,您会将应用程序的依赖项注册到“容器”中,然后可以使用该容器创建任何服务。这对于您自己的自定义应用程序服务和 ASP.NET Core 使用的框架服务都是如此。您必须在容器中注册每个服务,然后才能在应用程序中使用它。
注:我将在第10章详细描述 ASP.NET Core 中使用的依赖反转原理和IoC容器。
在 ASP.NET Core 应用程序中,此注册是在 ConfigureServices 方法中执行的。每当您在应用程序中使用新的 ASP.NET Core 功能时,都需要返回此方法并添加必要的服务。这并不像听起来那么困难,如下面的列表(取自示例应用程序)所示。
清单2.5 Startup.ConfigureServices:向 IoC 容器添加服务
public class Startup
{
// This method gets called by the runtime.
// Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
}
您可能会惊讶于,一个完整的 RazorPages 应用程序只包含一个必要服务的调用,但 AddRazorPage() 方法是一个扩展方法,它封装了设置 Razor Pages 服务所需的所有代码。在幕后,它添加了各种 Razor 服务,用于呈现HTML、格式化服务、路由服务等。
除了注册与框架相关的服务之外,此方法还可以注册应用程序中的任何自定义服务,例如前面讨论的示例 TaxCalculator。IServiceCollection 是应用程序需要使用的所有已知服务的列表。通过向其中添加新服务,可以确保每当类声明对服务的依赖时,IoC 容器都知道如何提供它。
所有服务都已配置完毕,现在是时候进入最后一个配置步骤了:定义应用程序如何响应 HTTP 请求。
2.7.2 定义如何使用中间件处理请求
到目前为止,在 IHostBuilder 和 Startup 类中,您已经定义了应用程序的基础结构,并向IoC容器注册了服务。在 Startup 类的最后一个配置方法 Configure中,您为应用程序定义中间件管道,该管道定义应用程序如何处理 HTTP 请求。下面是模板应用程序的 Configure 方法。
清单2.6 Startup.Configure:定义中间件管道
public class Startup
{
//IApplicationBuilder用于构建中间件管道。可以接受其他服务作为参数。
public void Configure( IApplicationBuilder app, IWebHostEnvironment env)
{
//开发或生产时的不同行为
if (env.IsDevelopment())
{
//仅在开发环境中运行
app.UseDeveloperExceptionPage();
}
else
{
//仅在生产环境中运行
app.UseExceptionHandler("/Error"); app.UseHsts();
}
app.UseHttpsRedirection();
//添加静态文件中间件
app.UseStaticFiles();
//添加端点路由中间件,该中间件决定要执行哪个端点
app.UseRouting();
//添加授权中间件,它可以根据需要阻止对特定页面的访问
app.UseAuthorization();
//添加端点中间件,该中间件执行RazorPage以生成HTML响应
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
}
}
}
如前所述,中间件由小组件组成,当应用程序接收到 HTTP 请求时,这些组件按顺序执行。它们可以执行一整套功能,例如日志记录、识别请求的当前用户、提供静态文件和处理错误。
传递给 Configure 方法的 IApplicationBuilder 用于定义中间件的执行顺序。此方法中调用的顺序很重要,因为它们添加到生成器的顺序就是它们在最终管道中执行的顺序。中间件只能在管道中使用以前的中间件创建的对象,而不能访问后来的中间件所创建的对象。
警告:在将中间件添加到管道中时,务必考虑中间件的顺序。中间件只能使用管道中早期中间件创建的对象。
您还应该注意,IWebHostEnvironment 参数用于在开发环境中提供不同的行为。当您在开发中运行时(当 EnvironmentName 设置为“development”时),Configure 方法将一个异常处理中间件添加到管道中;在生产中,它添加了一个不同的。
IWebHostEnvironment 对象包含由程序中的 IHostBuilde 确定的当前环境的详细信息。它公开了许多属性:
- ContentRootPath —— 应用程序的工作目录的位置,通常是运行应用程序的文件夹
- WebRootPath —— 包含静态文件的 wwwroot 文件夹的位置
- EnvironmentName —— 当前环境是开发环境还是生产环境
IWebHostEnvironment 在调用 Startup 时已设置;无法使用“Startup”中的应用程序设置更改这些值。EnvironmentName 通常在应用程序启动时使用环境变量从外部设置。
注:您将在第11章中了解托管环境以及如何更改当前环境。
在开发中,DeveloperExceptionPageMiddleware(由UseDeveloperExcessionPage() 调用添加)确保如果应用程序抛出未捕获的异常,浏览器中会显示尽可能多的信息来诊断问题,如图2.11所示,但这次是白色的,不是黄色的。
图2.11 开发人员异常页面包含许多不同的信息源以帮助您诊断问题,包括异常堆栈跟踪和生成异常的请求的详细信息。
注意:默认模板也会在生产中添加 HstsMiddleware,它会根据行业最佳实践在响应中设置安全标头。请参阅第18章,了解有关此和其他安全相关中间件的详细信息。
当您在生产环境中运行时,将大量数据暴露给用户将是一个巨大的安全风险。相反,ExceptionHandlerMiddleware 是注册的,这样,如果用户在您的方法中遇到异常,将向他们显示一个友好的错误页面,该页面不会显示问题的来源。如果在生产模式下运行默认模板并触发错误,则将显示图2.12所示的消息。显然,您需要更新此页面以使其更具视觉吸引力和用户友好,但至少它不会揭示应用程序的内部工作方式。
图2.12 默认异常处理页面。与开发人员异常页面不同的是,此页面不会向用户透露任何有关应用程序的详细信息。事实上,您可以将消息更新为更方便用户的内容。
添加到管道中的下一个中间件是 HttpsDirectionMiddleware,使用以下语句:
app.UseHttpsRedirection();
这确保您的应用程序只响应安全(HTTPS)请求,是行业最佳做法。我们将在第18章中详细介绍HTTPS。
接下来是以下语句,StaticFileMiddleware 将添加到管道中:
app.UseStaticFiles();
该中间件负责处理静态文件(如 CSS 文件、JavaScript 文件和图像)的请求。当请求到达中间件时,它会检查该请求是否针对现有文件。如果是,中间件将返回文件。如果没有,则忽略该请求,下一个中间件可以尝试处理该请求。图2.13显示了在请求静态文件时如何处理请求。当静态文件中间件处理请求时,管道中稍后出现的其他中间件(如路由中间件或端点中间件)根本不会被调用。
图2.13 /css/site 上的静态文件请求概述。css 用于 ASP.NET Core 应用程序。请求通过中间件管道,直到由静态文件中间件处理。这将返回请求的 CSS 文件作为响应,并将其传递回 Web 服务器。端点中间件从未被调用,也从未看到请求。
现在,我们来看看管道中最重要的中间件:路由中间件(RoutingMiddleware)和端点中间件(EndpointMiddleware)。这对中间件一起负责解释请求以确定要调用哪个 Razor Page,从请求中读取参数,并生成最终的HTML。需要非常简单的配置,您只需要将中间件添加到管道中,并通过调用 MapRazorPages 指定您希望使用 Razor Page 端点。对于每个请求,路由中间件使用请求的URL来确定要调用哪个 Razor Page。端点中间件实际上执行 Razor Page 以生成 HTML 响应。
注意:默认模板还将在路由中间件和端点中间件之间添加 AuthorizationMiddleware。这允许身份验证中间件在执行 RazorPage 之前决定是否允许访问。您将在关于路由的第5章和关于授权的第15章中了解有关此方法的更多信息。
哈哈!您终于完成了应用程序所需的所有设置、服务和中间件的配置。配置应用程序涉及广泛的内容,我们将在本书中进一步深入讨论,因此如果您还没有完全理解所有步骤,请不要担心。
一旦配置了应用程序,它就可以开始处理请求。但它是如何处理它们的?我已经提到了 StaticFileMiddleware,它将向用户提供图像和 CSS 文件,但是需要 HTML 响应的请求呢?在本章的其余部分,我将向您介绍 Razor Pages 及其如何生成 HTML。
2.8 使用 Razor Pages 生成响应
当 ASP.NET Core 应用程序收到请求时,它通过中间件管道前进,直到中间件组件能够处理它,如图2.13所示。通常,管道中的最后一个中间件是端点中间件。该中间件与路由中间件一起工作,以将请求 URL 的路径匹配到已配置的路由,该路由定义要调用哪个 Razor Page。
路径是请求 URL 的剩余部分。例如,对于发送到 www.microsoft.com/account/manage 的请求,路径为 /account/manage。
一旦选择了 Razor Page,路由中间件就会在请求的 HttpContext 中记录所选的 Razor Page,并继续执行中间件管道。最终,请求将到达端点中间件。端点中间件执行 Razor Page 以生成 HTML 响应并将其发送回浏览器,如图2.14所示。
图2.14 将 Razor 模板渲染为 HTML。RazorPage 是基于 URL 页面/ Privacy 选择的,并被执行以生成 HTML。
在下一节中,我们将研究 Razor Pages 如何使用 Razor 语法生成 HTML。之后,我们将研究如何使用页面处理程序将业务逻辑和行为添加到 Razor Pages。
2.8.1 使用 Razor Page 生成 HTML
Razor Pages 存储在项目 Pages 文件夹中的 .cshtml 文件(.cs 和 .html的组合)中。通常,路由中间件通过在项目的 Pages 文件夹中查找具有相同路径的 Razor Page,将请求 URL 路径映射到单个 RazorPage。例如,您可以在图2.14中看到,应用程序的 Privacy 页面对应于浏览器地址栏中的路径 /Privacy。如果您查看项目的 Pages 文件夹,您会发现 Privacy.cshtml 文件,如以下列表所示。
清单2.7 Privacy.cshtml Razor 页面
@page <!-- 指示这是RazorPage -->
@model PrivacyModel <!-- 将Razor Page链接到特定的PageModel -->
@{
ViewData["Title"] = "Privacy Policy"; <!-- 不写入响应的C#代码 -->
}
<h1>@ViewData["Title"]</h1> <!-- 将动态C#值写入响应的HTML -->
<p>Use this page to detail your site's privacy policy.</p> <!-- 独立的静态HTML -->
Razor Pages 使用名为 Razor 的模板语法,将静态 HTML 与动态 C# 代码和 HTML 生成相结合。Razor Page 第一行的 @page 指令是最重要的。该指令必须始终放在文件的第一行,因为它告诉 ASP.NET Core .cshtml 文件是 RazorPage。如果没有它,您将无法正确查看页面。
Razor Page 的下一行定义了 Razor Page 与项目中的哪个 PageModel 关联:
@model PrivacyModel
在这种情况下,PageModel 被称为 PrivacyModel,它遵循命名 Razor Page 模型的标准惯例。您可以在 Privacy.cshtml 中找到该文件,如图2.15所示。Visual Studio 将这些文件嵌套在 Solution Explorer 中的 RazorPage.cshtml 文件下面。我们将在下一节中查看页面模型。
图2.15 按照惯例,Razor Pages 的页面模型放置在与 RazorPage 同名的文件中,并附加 .cs 后缀。Visual Studio 将这些文件嵌套在解决方案资源管理器中的 Razor Page下。
除了 @page 和 @model 指令之外,您可以看到静态 HTML 在 RazorPage 中始终有效,并将在响应中“原样”呈现。
<p>Use this page to detail your site’s privacy policy.</p>
您还可以使用以下构造在 Razor 模板中编写普通的 C# 代码:
@{ /* C# code here */ }
大括号之间的任何代码都将被执行,但不会写入响应。在列表中,您通过向 ViewData 字典写入关键字来设置页面标题,但此时没有向响应写入任何内容:
@{
ViewData["Title"] = "Privacy Policy";
}
此模板中显示的另一个特性是,您可以使用@符号将 C# 变量动态写入 HTML 流。这种结合动态和静态标记的能力正是 Razor Pages 的强大之处。在本例中,您从 ViewData 字典中获取“Title”值,并将这些值写入<h1>标记中的响应:
<h1>@ViewData["Title"]</h1>
此时,当与图2.14所示的输出进行比较时,您可能会对清单2.7中的模板感到有点困惑。列表和图中都显示了标题和静态 HTML 内容,但最终网页的某些部分没有显示在模板中。这怎么可能?
Razor Pages 具有布局的概念,它是定义应用程序常见元素(如页眉和页脚)的“基本”模板。布局的 HTML 与 RazorPage 模板相结合,生成发送到浏览器的最终 HTML。这防止了您必须在每个页面中复制页眉和页脚的代码,这意味着,如果您需要调整某些内容,您只需要在一个位置进行调整。
注:我将在第7章详细介绍 Razor 模板,包括布局。您可以在项目的 Pages/Shared 文件夹中找到布局。
正如您已经看到的,您可以通过使用花括号 @{} 在 RazorPages 中包含C#代码,但一般来说,您希望将 .cshtml 文件中的代码仅限于显示内容。复杂的逻辑、访问服务(如数据库)的代码和数据操作应该在 PageModel 中处理。
2.8.2 使用 PageModel 和处理程序处理请求逻辑
正如您已经看到的,.cshtml 文件中的 @page 指令将页面标记为 Razor Page,但大多数 Razor Pages 也有关联的页面模型。按照惯例,这被放置在一个通常称为“code behind”文件的文件中,该文件具有 .cs 扩展名,如图2.15所示。页面模型应该派生自 PageModel 基类,它们通常包含一个或多个称为页面处理程序的方法,这些方法定义如何处理对 Razor Page 的请求。
定义:页面处理程序是响应请求而运行的方法。Razor Page 模型必须从 PageModel 类派生。它们可以包含多个页面处理程序,但通常只包含一个或两个。
下面的列表显示了 Privacy.cshtml RazorPage 的页面模型,位于文件 Privacy.cshtml.cs 中。
清单2.8 Privacy 中的 PrivacyModel.cshtmlcs —— RazorPage 页面模型
//RazorPages必须继承自PageModel。
public class PrivacyModel: PageModel
{
private readonly ILogger<PrivacyModel> _logger;
//可以使用依赖注入在构造函数中提供服务。
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
//默认的页面处理程序是OnGet。返回void表示应生成HTML。
public void OnGet()
{
}
}
这个页面模型非常简单,但它展示了几个重要的点:
- 页面处理程序由约定驱动。
- 页面模型可以使用依赖注入与其他服务交互。
页面处理程序通常根据其响应的 HTTP 谓词按惯例命名。它们返回 void(表示应呈现Razor Page的模板)或 IActionResult(包含生成响应的其他指令,例如将用户重定向到其他页面)。
定义:每个 HTTP 请求都包含一个表示请求“类型”的动词。浏览网站时,默认动词是 GET,它从服务器获取资源,以便您查看。第二个最常见的动词是 POST,它用于向服务器发送数据,例如在完成表单时。
PrivacyModel 包含一个处理程序 OnGet,该处理程序指示它应该响应页面的 GET 请求而运行。当方法返回 void 时,执行处理程序将为页面执行关联的 Razor 模板以生成 HTML。
注意:Razor Pages 专注于构建基于页面的应用程序,因此您通常希望返回 HTML 而不是 JSON 或 XML。但是,您也可以使用 IActionResult 返回任何类型的数据,将用户重定向到新页面,或发送错误。您将在第4章中了解有关 IActionResults 的更多信息。
依赖注入用于将 ILogger<PrivacyModel>实例注入到页面模型的构造函数中。此示例中未使用该服务,但它可以用于将有用信息记录到各种目的地,例如控制台、文件或远程日志记录服务。您可以通过在构造函数中接受其他服务作为参数来访问页面模型中的其他服务。ASP.NET Core 框架将负责配置和注入您请求的任何服务的实例。
注:我在第10章详细描述了 ASP.NET Core 中使用的依赖性反转原理和 IoC 容器。第17章介绍了日志记录。
显然,PrivacyModel 页面模型在这种情况下做不了什么,您可能会想为什么它到底有什么用处。如果他们所做的只是告诉 Razor Page 生成 HTML,那么我们为什么需要页面模型呢?
这里要记住的关键点是,您现在有了一个框架,可以响应请求执行任意复杂的功能。您可以很容易地更新 handler 方法以从数据库加载数据、发送电子邮件、将产品添加到购物篮或创建发票,所有这些都是响应简单的 HTTP 请求。这种可扩展性是 Razor Pages(以及 MVC 模式)的强大之处。
另一个重要的点是,您将这些方法的执行与 HTML 本身的生成分离开来。如果逻辑发生变化,并且需要向页面处理程序添加行为,则不需要修改 HTML 生成代码,因此不太可能引入错误。相反,如果需要稍微更改 UI,例如更改标题的颜色,则处理程序方法逻辑是安全的。
这就是一个完整的 ASP.NET Core Razor Pages 应用程序!在继续之前,让我们最后看一下应用程序如何处理请求。图2.16显示了示例应用程序正在处理的对 /Privacy 路径的请求。您已经看到了这里的所有内容,因此处理请求的过程应该很熟悉。它显示了请求在被端点中间件处理之前如何通过中间件管道。Privacy.cshtml RazorPage 执行 OnGet 处理程序并生成 HTML 响应,该响应在发送到用户浏览器之前通过中间件传递回 ASP.NET Core Web 服务器。
这是一次非常快速的旅程,但现在您可以很好地了解整个应用程序是如何配置的,以及它如何使用 RazorPages 处理请求。在下一章中,您将进一步了解所有 ASP.NET Core 应用程序中存在的中间件管道。您将了解它是如何组成的,如何使用它为应用程序添加功能,以及如何使用它创建简单的HTTP服务。
图2.16 示例 ASP.NET RazorPages 应用程序的 /Privacy URL 请求概述。路由中间件将请求路由到 Privacy.cshtml.cs RazorPage 的 OnGet 处理程序。RazorPage 通过在 Privacy.cshtml 中执行 Razor 模板生成 HTML 响应,并通过中间件管道将响应传递回浏览器。
总结
- .csproj 文件包含如何构建项目的详细信息,包括它所依赖的 NuGet 包。Visual Studio 和 .NET CLI 使用它来构建应用程序。
- 恢复 ASP.NET Core 应用程序的 NuGet 包将下载项目的所有依赖项,以便可以构建和运行。
- Program.cs 定义应用程序的静态 void Main 入口点。此功能在应用程序启动时运行,与控制台应用程序相同。
- Program.cs 是使用 IHostBuilder 构建 IHost 实例的地方。助手方法 HostCreateDefaultBuilder() 创建一个 IHostBuilder,用于加载配置设置并设置日志记录。调用 Build() 将创建 IHost 实例。
- ConfigureWebHostDefaults 扩展方法使用 WebHostBuilder 配置通用主机。它配置 Kestrel HTTP 服务器,必要时添加 IIS 集成,并指定应用程序的 Startup 类。
- 您可以通过在 IHost 上调用 Run 来启动 Web 服务器并开始接受 HTTP 请求。
- Startup 负责服务配置和定义中间件管道。
- 所有服务(包括框架服务和自定义应用程序服务)必须在 ConfigureServices 调用中注册,以便稍后在应用程序中访问。
- 中间件通过 IApplicationBuilder 添加到应用程序管道中。中间件定义应用程序如何响应请求。
- 中间件的注册顺序定义了应用程序中间件管道的最终顺序。通常,EndpointMiddleware 是管道中的最后一个中间件。早期的中间件,如 StaticFileMiddleware,将尝试首先处理请求。如果处理了请求,EndpointMiddleware 将永远不会接收到请求。
- Razor Pages 位于 Pages 文件夹中,通常根据其处理的 URL 路径命名。例如,Privacy.cshtml 处理路径 /Privacy。
- Razor Pages 必须包含 @page 指令作为文件的第一行。
- 页面模型派生自 PageModel 基类并包含页面处理程序。页面处理程序是使用指示其处理的 HTTP 谓词的约定命名的方法。例如,OnGet 处理 GET 动词。
- Razor 模板可以包含独立的 C#、独立的 HTML 和从 C# 值生成的动态 HTML。通过将这三者结合起来,您可以构建高度动态的应用程序。
- Razor 布局定义网页的常见元素,如页眉和页脚。它们允许您将此代码提取到一个文件中,因此您不必在每个 Razor 模板中复制它。