了解中间件

已完成

Web 应用程序的目的是接收和响应 HTTP 请求。 收到请求后,服务器会生成适当的响应。 ASP.NET Core 中的一切都与此请求/响应周期有关。

当 ASP.NET Core 应用收到 HTTP 请求时,它会通过一系列组件来生成响应。 这些组件称为中间件。 中间件可以被视为请求流经的管道,并且每个中间件层都可以在管道中的下一层之前和之后运行代码。

描述 HTTP 请求通过多个中间件时的流程图。

中间件和委托

系统会以委托的形式实现中间件,其会接收 HttpContext 对象并返回 TaskHttpContext 对象表示当前请求和响应。 委托是处理请求和响应的函数。

例如,考虑以下代码:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello world!");
});

app.Run();

在上述代码中:

  • WebApplication.CreateBuilder(args) 会新建 WebApplicationBuilder 对象。
  • builder.Build() 会新建 WebApplication 对象。
  • 第一个 app.Run() 会定义一个委托,该委托接受 HttpContext 对象并返回 Task。 委托将“Hello world!”写入响应。
  • 第二个 app.Run() 会启动应用。

当应用收到 HTTP 请求时,将调用委托。 委托将“Hello world!”写入响应并完成请求。

链接中间件

在大多数应用中,有多个按顺序运行的中间件组件。 将中间件组件添加到管道的顺序非常重要。 组件按照添加的顺序运行。

终端和非终端中间件

每个中间件都可以视为终端或非终端中间件。 非终端中间件会处理请求,然后调用管道中的下一个中间件。 终端中间件是管道中的最后一个中间件,没有下一个要调用的中间件。

使用 app.Use() 添加的委托可以是终端或非终端中间件。 这些委托需要一个 HttpContext 对象和一个 RequestDelegate 对象作为参数。 通常情况下,委托包括 await next.Invoke();。 这会将控制权传递给管道上的下一个中间件。 该行之前的代码在下一个中间件之前运行,该行之后的代码在下一个中间件之后运行。 使用 app.Use() 添加的委托在响应发送到客户端之前有两次机会对请求采取行动;一次是在终端中间件生成响应之前,另一次是在终端中间件生成响应之后。

使用 app.Run() 添加的委托始终是终端中间件。 不会调用管道中的下一个中间件。 是运行的最后一个中间件组件。 只需要 HttpContext 对象作为参数。 app.Run() 是添加终端中间件的快捷方式。

请考虑以下示例:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Hello from middleware 1. Passing to the next middleware!\r\n");

    // Call the next middleware in the pipeline
    await next.Invoke();

    await context.Response.WriteAsync("Hello from middleware 1 again!\r\n");
});

app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from middleware 2!\r\n");
});

app.Run();

在上述代码中:

  • app.Use() 定义了一个中间件组件,该组件将执行以下操作:
    • 在响应中写入“Hello from middleware 1. Passing to the next middleware!”。
    • 将请求传递给管道中的下一个中间件组件,并等待它使用 await next.Invoke() 完成。
    • 在管道中的下一个组件完成后,它会写入“Hello from middleware 1 again!”
  • 第一个 app.Run() 会定义一个中间件组件,该组件在响应中写入“Hello from middleware 2!”。
  • 第二个 app.Run() 会启动应用。

在运行时,当 Web 浏览器向此应用程序发送请求时,中间件组件将按照添加到管道的顺序运行。 应用返回以下响应:

Hello from middleware 1. Passing to the next middleware!
Hello from middleware 2!
Hello from middleware 1 again!

内置中间件

ASP.NET Core 提供了一组内置中间件组件,可用于向应用添加通用功能。 除了明确添加的中间件组件之外,还会默认为你隐式添加一些中间件。 例如,WebApplication.CreateBuilder() 会返回 WebApplicationBuilder,系统会在后者中添加开发人员异常页路由中间件和终结点路由中间件,如果配置了相关服务,还会根据情况添加身份验证和授权中间件。

例如,请考虑以下 Program.cs 文件:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseAntiforgery();

app.MapStaticAssets();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();

在上述代码中:

  • app.UseExceptionHandler() 添加一个中间件组件,该组件捕获异常并返回错误页。
  • app.UseHsts() 添加一个中间件组件,用于设置 Strict-Transport-Security 标头。
  • app.UseHttpsRedirection() 添加一个中间件组件,将 HTTP 请求重定向到 HTTPS。
  • app.UseAntiforgery() 添加一个中间件组件,用于防止跨网站请求伪造 (CSRF) 攻击。
  • app.MapStaticAssets()app.MapRazorComponents<App>() 将路由映射到终结点,然后由终结点路由中间件处理。 终结点路由中间件由 WebApplicationBuilder 隐式添加。

根据应用类型和需求,可以在应用中使用更多内置中间件组件。 查看完整列表的文档

提示

在此上下文中,以 Use 开头的方法通常用于映射中间件。 以 Map 开头的方法通常用于映射终结点。

重要

将中间件组件添加到管道的顺序很重要! 某些中间件组件必须在其他组件正常运行之前运行。 检查每个中间件组件的文档以确定正确的顺序。