一、什么是Middleware
ASP.NET Core中间件组件是被组装到应用程序管道中以处理HTTP请求和响应的软件组件,每一个中间件组件都执行以下任务:
- 选择是否将HTTP请求传递给管道中的下一个组件。这可以通过在中间件中调用next()方法实现。
- 可以在管道中的下一个组件之前和之后执行工作。
在ASP.NET Core中,已经有很多内置的中间件组件可供使用,详见官网:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0。如果需要,还可以自定义中间件。
中间件 | 描述 | 顺序 |
---|---|---|
身份验证 | 提供身份验证支持。 | 在需要 HttpContext.User 之前。 OAuth 回叫的终端。 |
授权 | 提供身份验证支持。 | 紧接在身份验证中间件之后。 |
Cookie 策略 | 跟踪用户是否同意存储个人信息,并强制实施 cookie 字段(如 secure 和 SameSite )的最低标准。 | 在发出 cookie 的中间件之前。 示例:身份验证、会话、MVC (TempData)。 |
CORS | 配置跨域资源共享。 | 在使用 CORS 的组件之前。 由于此错误,UseCors 当前必须在 UseResponseCaching 之前运行。 |
诊断 | 提供新应用的开发人员异常页、异常处理、状态代码页和默认网页的几个单独的中间件。 | 在生成错误的组件之前。 异常终端或为新应用提供默认网页的终端。 |
转接头 | 将代理标头转发到当前请求。 | 在使用已更新字段的组件之前。 示例:方案、主机、客户端 IP、方法。 |
运行状况检查 | 检查 ASP.NET Core 应用及其依赖项的运行状况,如检查数据库可用性。 | 如果请求与运行状况检查终结点匹配,则为终端。 |
标头传播 | 将 HTTP 标头从传入的请求传播到传出的 HTTP 客户端请求中。 | |
HTTP 方法重写 | 允许传入 POST 请求重写方法。 | 在使用已更新方法的组件之前。 |
HTTPS 重定向 | 将所有 HTTP 请求重定向到 HTTPS。 | 在使用 URL 的组件之前。 |
HTTP 严格传输安全性 (HSTS) | 添加特殊响应标头的安全增强中间件。 | 在发送响应之前,修改请求的组件之后。 示例:转接头、URL 重写。 |
MVC | 用 MVC/Razor Pages 处理请求。 | 如果请求与路由匹配,则为终端。 |
OWIN | 与基于 OWIN 的应用、服务器和中间件进行互操作。 | 如果 OWIN 中间件处理完请求,则为终端。 |
响应缓存 | 提供对缓存响应的支持。 | 在需要缓存的组件之前。 UseCORS 必须在 UseResponseCaching 之前。 |
响应压缩 | 提供对压缩响应的支持。 | 在需要压缩的组件之前。 |
请求本地化 | 提供本地化支持。 | 在对本地化敏感的组件之前。 |
终结点路由 | 定义和约束请求路由。 | 用于匹配路由的终端。 |
SPA | 通过返回单页应用程序 (SPA) 的默认页面,在中间件链中处理来自这个点的所有请求 | 在链中处于靠后位置,因此其他服务于静态文件、MVC 操作等内容的中间件占据优先位置。 |
会话 | 提供对管理用户会话的支持。 | 在需要会话的组件之前。 |
静态文件 | 为提供静态文件和目录浏览提供支持。 | 如果请求与文件匹配,则为终端。 |
URL 重写 | 提供对重写 URL 和重定向请求的支持。 | 在使用 URL 的组件之前。 |
WebSockets | 启用 WebSockets 协议。 | 在接受 WebSocket 请求所需的组件之前。 |
二、如何在ASP.NET Core应用程序中配置中间件组件
在ASP.NET Core应用程序中,我们需要在Startup类的Configure()方法内配置中间件组件。当使用空模板创建ASP.NET Core应用程序时,默认情况下使用了如下的中间件:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
2.1 UseDeveloperExceptionPage
当托管环境设置为“Development”时,该中间件组件注册到管道中。当应用程序中发生未处理的异常时,该中间件将执行,并且由于它处于开发模式,因此他将显示代码的错误信息。
2.2 UseRouting和UseEndpoints
路由使用一对由UseRouting和UseEndpoints注册的中间件:
- UseRouting:向中间件管道添加路由匹配,此中间件会查看应用中定义的终结点集,并根据请求选择最佳匹配。
- UseEndpoints:向中间件管道添加终结点执行,它会运行与所选终结点关联的委托。
除了MapGet方法,还可以使用Map方法,MapGet方法将处理Get Http请求,而Map方法将处理所有类型的HTTP请求,例如GET、POST、PUT和DELETE等。
app.UseEndpoints(endpoints =>
{
endpoints.Map("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
三、Use and Run方法的用途是什么?
可以使用“Use”和“Run”拓展方法将内联中间件组件注册到请求处理管道中。“Run”拓展方法使我们可以添加终止中间件(不会在请求处理管道中调用下一个中间件)。
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello World!");
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello Baby!");
});
}
上述代码会在第一个Run执行完了后就终止,原因是,当我们使用Run拓展方法注册中间件组件时,该组件成为终端组件,这意味着它不会再请求处理管道中调用下一个中间件。这个时候就需要用到Use方法。
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context,next) =>
{
await context.Response.WriteAsync("Hello World!");
await next();
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello Baby!");
});
}
四、请求处理管道
ASP.NET Core中间件的执行顺序与添加到管道中的顺序相同。
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Middleware1:Incoming Request\n");
await next();
await context.Response.WriteAsync("Middleware1:Outgoing Response\n");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Middleware2:Incoming Request\n");
await next();
await context.Response.WriteAsync("Middleware2:Outgoing Response\n");
});
app.Run(async context =>
{
await context.Response.WriteAsync("Middleware3:Incoming Request handled and respose generated\n");
});
}