Loading

ASP.NET Core 实战-2.你的第一个应用程序

ASP.NET Core 应用程序的简要概述

假设您要创建一个 Web 应用程序来显示有关您公司的信息。 您可以创建一个简单的 ASP.NET Core 应用程序来实现这一点; 然后,稍后,您可以为您的应用添加动态功能。 如图显示了应用程序如何处理应用程序中的页面请求。

ASP.NET Core 应用程序接收来自浏览器的传入HTTP请求。每个请求都传递到中间件管道,中间件管道可能会对其进行修改,然后将其传递到管道末端的端点中间件以生成响应。响应通过中间件传回服务器,最后传给浏览器。
image

创建您的第一个 ASP.NET Core 应用程序

要创建您的第一个 Web 应用程序,请打开 Visual Studio 并执行以下步骤:

  1. 从初始屏幕中选择“创建新项目”,或从 Visual Studio 主屏幕中选择“文件”>“新建”>“项目”。

  2. 从模板列表中,选择 ASP.NET Core Web Application,确保选择 C# 语言模板,如图 所示。 点击下一步。

    图 2.2 新建项目对话框。 从右侧的列表中选择 C# ASP.NET Core Web 应用程序模板。 下次创建新项目时,您可以从左侧的最近模板列表中进行选择。
    image
  3. 在下一个屏幕上,输入项目名称、位置和解决方案名称,然后单击“创建”,如图所示。 例如,使用 WebApplication1 作为两者 项目和解决方案名称。

    image
  4. 在以下屏幕上(下图):

image

使用 .NET CLI 创建新的 Razor Page 应用程序

dotnet new sln -n WebApplication1
dotnet new webapp -o WebApplication1
dotnet sln add WebApplication1

.csproj 项目文件,显示 SDK、目标框架和参考

<!--SDK 属性指定您正在构建的项目的类型。-->
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
	  <!--TargetFramework 是您将运行的框架,在本例中为 .NET 3.1。-->
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

</Project>

默认 Program.cs 文件配置并运行 IWebHost

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace WebApplication1
{
    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。

您的应用程序的大部分配置发生在通过调用 CreateDefaultBuilder 创建的 IHostBuilder 中,但它将一些责任委托给了一个单独的类 Startup。 通用 UseStartup<> 方法中引用的 Startup 类是您配置应用服务和定义中间件管道的地方。

下图显示了 Program 和 Startup 之间配置组件的典型拆分。 一般来说,程序是您配置应用程序基础结构的地方,例如 HTTP 服务器、与 IIS 的集成以及配置源。 相反,在 Startup 中,您可以定义应用程序使用的组件和功能,以及应用程序的中间件管道。

Program 和 Startup 的配置范围不同。 程序关注基础设施配置,这些配置通常在项目的整个生命周期内保持稳定。 相反,您会经常修改 Startup 以添加新功能并更新应用程序行为。
image

两个不同 ASP.NET Core 应用程序的 Program 类通常相似,但 Startup 类通常会有很大不同(尽管它们通常遵循相似的模式,您很快就会看到)。 随着应用程序的增长,您很少会发现需要修改 Program,而通常会在添加其他功能时更新 Startup。 例如,如果向项目添加新的 NuGet 依赖项,通常需要更新 Startup 才能使用它。

Program 类是进行大量应用程序配置的地方,但在默认模板中,这隐藏在 CreateDefaultBuilder 方法中。 CreateDefaultBuilder 是一种静态辅助方法,它通过创建具有一些通用配置的 IHostBuilder 来简化应用程序的引导。

默认使用的另一个辅助方法是 ConfigureWebHostDefaults。 这使用 WebHostBuilder 对象来配置 Kestrel 以侦听 HTTP 请求。

完成 IHostBuilder 的配置后,对 Build 的调用会生成 IHost 实例,但应用程序仍未处理 HTTP 请求。 是对 Run 的调用启动了 HTTP 服务器的侦听。 此时,您的应用程序已完全运行,可以响应来自远程浏览器的第一个请求。

启动类:配置您的应用程序

如您所见,Program 负责为您的应用程序配置大量基础架构,但您在 Startup 中配置应用程序的一些行为。 Startup 类负责配置应用程序的两个主要方面:

  • 服务注册——您的应用程序依赖于提供功能的任何类(包括框架使用的类和特定于您的应用程序的类)都必须注册,以便它们可以在运行时正确实例化。
  • 中间件和端点——您的应用程序如何处理和响应请求。

您可以在 Startup 中以自己的方法配置这些方面中的每一个:ConfigureServices 中的服务注册,以及 Configure 中的中间件配置。 下面的清单显示了一个典型的 Startup 大纲。

Startup.cs 的大纲显示了如何配置每个方面

public class Startup
{
    //通过向 IServiceCollection 注册服务来配置服务。
    public void ConfigureServices(IServiceCollection services)
    {
        // method details
    }
    //配置中间件管道以处理 HTTP 请求。
    public void Configure(IApplicationBuilder app)
    {
        // method details
    }
}

Program中创建的IHostBuilder调用ConfigureServices,再调用Configure,如图所示。 每个调用配置应用程序的不同部分,使其可用于后续方法调用。 在 ConfigureServices 方法中注册的任何服务都可用于 Configure 方法。 配置完成后,通过在 IHostBuilder 上调用 Build() 创建 IHost

IHostBuilderProgram.cs 中创建,并调用 Startup 上的方法来配置应用程序的服务和中间件管道。 配置完成后,通过在 IHostBuilder 上调用 Build() 创建 IHost
image

关于 Startup 类的一个有趣的点是它没有实现这样的接口。 相反,这些方法是通过使用反射来调用的,以查找具有预定义名称 ConfigureConfigureServices 的方法。 这使类更加灵活,并使您能够修改方法的签名以接受自动实现的附加参数。

添加和配置服务

ASP.NET Core 使用小型模块化组件来实现每个不同的功能。 这允许单个功能单独发展,与其他功能只有松散耦合,这通常被认为是良好的设计实践。 这种方法的缺点是它把正确实例化特征的负担放在了消费者身上。 在您的应用程序中,这些模块化组件公开为应用程序使用的一项或多项服务。

在 ASP.Net Core 的上下文中,服务是指为应用程序提供功能的任何类。 这些可能是您为应用程序编写的库或代码公开的类。

在 ASP.NET Core 应用程序中,此注册在 ConfigureServices 方法中执行。 每当您在应用程序中使用新的 ASP.NET Core 功能时,您都需要返回此方法并添加必要的服务。 这并不像听起来那么困难,如下面的清单所示,取自示例应用程序。

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();
    }
}

您可能会感到惊讶,一个完整的 Razor Pages 应用程序只包含一个调用来添加必要的服务,但 AddRazorPages() 方法是一个扩展方法,它封装了设置 Razor Pages 服务所需的所有代码。在幕后,它添加用于呈现 HTML、格式化服务、路由服务等的各种 Razor 服务。

定义如何使用中间件处理请求

到目前为止,在 IHostBuilderStartup 类中,您已经定义了应用程序的基础架构并在 IoC 容器中注册了您的服务。 在 Startup 类的最终配置方法 Configure 中,您定义了应用程序的中间件管道,它定义了您的应用程序如何处理 HTTP 请求。 这是模板应用程序的配置方法。

Startup.Configure:定义中间件管道

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, //IApplicationBuilder 用于构建中间件管道。
                          IWebHostEnvironment env) //可以接受其他服务作为参数。
    {
        //在开发或生产中的不同行为
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage(); //仅在开发环境中运行
        }
        else //仅在生产环境中运行
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        //添加静态文件中间件
        app.UseStaticFiles();

        //添加端点路由中间件,该中间件确定要执行的端点
        app.UseRouting();

        //添加授权中间件,可根据需要屏蔽特定页面的访问
        app.UseAuthorization();

        app.UseEndpoints(endpoints => //添加端点中间件,该中间件执行 Razor 页面以生成 HTML 响应
                         {
                             endpoints.MapRazorPages();
                         });
    }
}

正如我之前所描述的,中间件由小组件组成,当应用程序接收到 HTTP 请求时,这些组件会按顺序执行。 它们可以执行大量功能,例如日志记录、识别请求的当前用户、提供静态文件和处理错误。

传递给 Configure 方法的 IApplicationBuilder 用于定义中间件的执行顺序。 此方法中调用的顺序很重要,因为它们添加到构建器的顺序就是它们在最终管道中执行的顺序。 中间件只能使用管道中以前的中间件创建的对象——它不能访问后面的中间件创建的对象。

您还应该注意,当您在开发环境中时,IWebHostEnvironment 参数用于提供不同的行为。 当您在开发中运行时(当 EnvironmentName 设置为“Development”时),Configure 方法会在管道中添加一个异常处理中间件; 在生产中,它添加了一个不同的。

IWebHostEnvironment 对象包含有关当前环境的详细信息,由 Program 中的 IHostBuilder 确定。 它公开了许多属性:

  • ContentRootPath - 应用程序工作目录的位置,通常是应用程序运行所在的文件夹
  • WebRootPath - 包含静态文件的 wwwroot 文件夹的位置
  • EnvironmentName - 当前环境是开发环境还是生产环境

IWebHostEnvironment 已在 Startup 被调用时设置; 您无法使用启动中的应用程序设置更改这些值。 EnvironmentName 通常在应用程序启动时使用环境变量在外部设置。

在开发中,DeveloperExceptionPageMiddleware(由 UseDeveloperExceptionPage() 调用添加)确保如果您的应用程序抛出未被捕获的异常,您将在浏览器中看到尽可能多的信息来诊断问题,如图所示。 类似于之前版本的 ASP.NET 中的“黄屏死机”,但这次是白色的,而不是黄色的。

开发人员异常页面包含许多不同的信息来源,可帮助您诊断问题,包括异常堆栈跟踪和有关生成异常的请求的详细信息。
image

当您在生产环境中运行时,将这么多数据暴露给用户将是一个很大的安全风险。 相反,注册了 ExceptionHandlerMiddleware,这样如果用户在您的方法中遇到异常,他们将看到一个友好的错误页面,该页面不会揭示问题的根源。 如果您在生产模式下运行默认模板并触发错误,您将看到如图 所示的消息。 显然,您需要更新此页面以使其更具视觉吸引力和用户友好性,但至少它不会揭示您的应用程序的内部工作原理。

默认的异常处理页面。 与开发人员例外页面相比,此页面不会向用户透露有关您的应用程序的任何详细信息。 实际上,您会将消息更新为更用户友好的内容。
image

使用以下语句将 StaticFileMiddleware 添加到管道中:

app.UseStaticFiles();

该中间件负责处理对 CSS 文件、JavaScript 文件和图像等静态文件的请求。 当请求到达中间件时,它会检查请求是否针对现有文件。 如果是,则中间件返回文件。 如果不是,则忽略该请求,并且下一个中间件可以尝试处理该请求。下图显示了在请求静态文件时如何处理请求。 当静态文件中间件处理请求时,管道中稍后出现的其他中间件,例如路由中间件或端点中间件,根本不会被调用。

ASP.NET Core 应用程序的 /css/site.css 中的静态文件请求概述。请求通过中间件管道,直到由静态文件中间件处理。 这会返回请求的 CSS 文件作为响应,该响应会传回 Web 服务器。 端点中间件永远不会被调用,也永远不会看到请求。
image

现在我们来看看管道中最重要的中间件:路由中间件和端点中间件。 这对中间件一起负责解释请求以确定调用哪个 Razor 页面、从请求中读取参数以及生成最终的 HTML。 只需要很少的配置 - 您只需将中间件添加到管道并通过调用 MapRazorPages 指定您希望使用 Razor Page 端点。 对于每个请求,路由中间件使用请求的 URL 来确定要调用哪个 Razor 页面。 端点中间件实际上执行 Razor 页面以生成 HTML 响应。

使用 Razor Pages 生成响应

使用 Razor 页面生成 HTML

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

将 Razor 模板呈现为 HTML。 Razor Page 是根据 URL 页面 /Privacy 选择的,并被执行以生成 HTML。
image

清单 2.7 Privacy.cshtml Razor 页面

@page   @*表示这是一个 Razor 页面*@
@model PrivacyModel  @* 将 Razor 页面链接到特定的 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 页面使用称为 Razor 的模板语法,它将静态 HTML 与动态 C# 代码和 HTML 生成相结合。 Razor 页面第一行的 @page 指令是最重要的。 该指令必须始终放在文件的第一行,因为它告诉 ASP.NET Core .cshtml 文件是 Razor 页面。 没有它,您将无法正确查看您的页面。

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

@model PrivacyModel

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

按照惯例,Razor 页面的页面模型放置在与 Razor 页面同名并附加 .cs 后缀的文件中。 Visual Studio 将这些文件嵌套在解决方案资源管理器的 Razor 页面下。
image

除了 @page 和 @model 指令之外,您还可以看到静态 HTML 在 Razor 页面中始终有效,并将在响应中“按原样”呈现。

<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>@ViewData["Title"]</h1>

正如您已经看到的,您可以使用花括号 @{ } 在 Razor 页面中包含 C# 代码,但一般来说,您希望将 .cshtml 文件中的代码限制为仅用于表示问题。 复杂的逻辑、访问数据库等服务的代码和数据操作应该在 PageModel 中处理。

使用 PageModels 和处理程序处理请求逻辑

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

Privacy.cshtml.cs 中的 PrivacyModel — Razor 页面页面模型

public class PrivacyModel: PageModel //Razor Pages 必须从 PageModel 继承。
{
    //您可以使用依赖注入在构造函数中提供服务。
    private readonly ILogger<PrivacyModel> _logger;
    public PrivacyModel(ILogger<PrivacyModel> logger)
    {
        _logger = logger;
    }
    //默认页面处理程序是 OnGet。 返回 void 表示应该生成 HTML。
    public void OnGet()
    {
    }
}

这个页面模型非常简单,但它展示了几个要点:

  • 页面处理程序由约定驱动。
  • 页面模型可以使用依赖注入与其他服务交互。

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

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

依赖注入用于将 ILogger 实例注入到页面模型的构造函数中。 此示例中未使用该服务,但它可用于将有用信息记录到各种目标,例如控制台、文件或远程日志记录服务。 您可以通过在构造函数中接受它们作为参数来访问页面模型中的其他服务——ASP.NET Core 框架将负责配置和注入您请求的任何服务的实例。

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

对示例 ASP.NET Razor Pages 应用程序的 /Privacy URL 的请求概述。 路由中间件将请求路由到 Privacy.cshtml.cs Razor 页面的 OnGet 处理程序。 Razor 页面通过执行 Privacy.cshtml 中的 Razor 模板生成 HTML 响应,并通过中间件管道将响应传递回浏览器。
image

总结

  • .csproj 文件包含有关如何构建项目的详细信息,包括它所依赖的 NuGet 包。 Visual Studio 和 .NET CLI 使用它来构建您的应用程序。
  • 恢复 ASP.NET Core 应用程序的 NuGet 包会下载项目的所有依赖项,以便可以构建和运行它。
  • Program.cs 为您的应用程序定义静态 void Main 入口点。 此函数在您的应用程序启动时运行,与控制台应用程序相同。
  • Program.cs 是您使用 IHostBuilder 构建 IHost 实例的地方。辅助方法 Host.CreateDefaultBuilder() 创建了一个加载配置设置并设置日志记录的 IHostBuilder。 调用 Build() 创建 IHost 实例。
  • ConfigureWebHostDefaults 扩展方法使用 WebHostBuilder 配置通用主机。它配置 Kestrel HTTP 服务器,必要时添加 IIS 集成,并指定应用程序的 Startup 类。
  • 您可以通过在 IHost 上调用 Run 来启动 Web 服务器并开始接受 HTTP 请求。
  • Startup 负责服务配置和定义中间件管道。
  • 所有服务,包括框架服务和自定义应用程序服务,都必须在对 ConfigureServices 的调用中注册,以便稍后在您的应用程序中访问。
  • 使用 IApplicationBuilder 将中间件添加到应用程序管道。 中间件定义了您的应用程序如何响应请求。
  • 注册中间件的顺序定义了应用程序中间件管道的最终顺序。 通常,EndpointMiddleware 是管道中的最后一个中间件。 早期的中间件,例如 StaticFileMiddleware,将尝试首先处理请求。 如果请求被处理,EndpointMiddleware 将永远不会收到请求。
  • Razor 页面位于 Pages 文件夹中,通常根据它们处理的 URL 路径命名。 例如,Privacy.cshtml 处理路径 /Privacy。
  • Razor 页面必须包含 @page 指令作为文件的第一行。
  • 页面模型派生自 PageModel 基类并包含页面处理程序。页面处理程序是使用指示它们处理的 HTTP 动词的约定命名的方法。 例如,OnGet 处理 GET 动词。
  • Razor 模板可以包含独立的 C#、独立的 HTML 和从 C# 值生成的动态 HTML。 通过结合这三者,您可以构建高度动态的应用程序。
  • Razor 布局定义网页的常见元素,例如页眉和页脚。 它们允许您将此代码提取到单个文件中,因此您不必在每个 Razor 模板中复制它。
posted @ 2022-09-03 17:08  F(x)_King  阅读(559)  评论(0编辑  收藏  举报