中间件是一种装配到应用管道以处理请求和响应的组件,每个组件:
- 选择是否将请求传递到管道中的下一个组件
- 可在管道中的下一个组件前后执行工作
请求委托用于生成请求管道。请求委托处理每个HTTP请求。
使用RunMap
和Use
扩展方法来配置请求委托。可将一个单独的请求委托并行指定为匿名方法(称为并行中间件),或在可重用的类中对其进行定义。这些可重用的类和并行匿名方法即为中间件,也叫做中间件组件。请求管道中的每个中间件组件负责调用管道中的下一个组件,或使管道短路。当中间件短路时,它被称为“终端中间件”,因为它阻止中间件进一步处理请求。
ASP.NET Core 请求管道包含一系列请求委托,依次调用。 下图演示了这一概念。 沿黑色箭头执行。
通过以下示例观察中间件的执行过程
1 | var builder = WebApplication.CreateBuilder(args); |
每个委托均可在下一个委托前后执行操作。应尽早在管道中调用异常处理委托,这样它们就能捕获在管道的后期阶段发生的异常。
Use委托
用Use
将多个请求委托链接在一起。next参数表示管道中的下一个委托。可通过不调用next参数使管道短路。通常可在next委托前后执行操作,如以下示例所示:
1 | var builder = WebApplication.CreateBuilder(args); |
Run委托
Run
委托不会收到next参数。第一个Run
委托始终为终端,用于终止管道。Run
时一种约定。某些中间件组件可能会公开在管道末尾运行的*Run[Middleware]***方法:
1 | var builder = WebApplication.CreateBuilder(args); |
在上面的示例中,Run
委托将 “Hello from 2nd delegate.” 写入响应,然后终止管道。 如果在 Run
委托之后添加了另一个 Use
或 Run
委托,则不会调用该委托。
Map委托
Map
扩展用作约定来创建管道分支。Map
基于给定请求路径的匹配项来创建请求管道分支。如果请求路径以给定路径开头,则执行分支。
1 | var builder = WebApplication.CreateBuilder(args); |
下表使用前面的代码显示来自 http://localhost:1234 的请求和响应。
请求 | 响应 |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
使用Map
时,将从HttpRequest.Path
中删除匹配的路径段,并针对每个请求将该路径段追加到HttpRequest.PathBase
Map嵌套
1 | app.Map("/level1", level1App => { |
Map匹配多段
1 | app.Map("/map1/seg1", app => { |
MapWhen委托
MapWhen
基于给定谓词的结果创建请求管道分支。 Func<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。 在以下示例中,谓词用于检测查询字符串变量 branch 是否存在:
1 | app.MapWhen(context => context.Request.Query.ContainsKey("branch"), app => |
下表使用前面的代码显示来自 http://localhost:1234 的请求和响应:
请求 | 响应 |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/?branch=main | Branch used = main |
中间件顺序
下图显示了 ASP.NET Core MVC 和 Razor Pages 应用的完整请求处理管道。可以在典型应用中了解现有中间件的顺序,以及在哪里添加自定义中间件。 可以完全控制如何重新排列现有中间件,或根据场景需要注入新的自定义中间件。
向 Program.cs 文件中添加中间件组件的顺序定义了针对请求调用这些组件的顺序,以及响应的相反顺序。 此顺序对于安全性、性能和功能至关重要。
Program.cs 中的以下突出显示的代码按照典型的建议顺序增加与安全相关的中间件组件:
1 | var builder = WebApplication.CreateBuilder(args); |
在上述代码中,并非所有中间件都完全按照此顺序出现,但许多中间件都会遵循此顺序。例如:
UseCors
、UseAuthentication
和UseAuthorization
必须按显示的顺序出现。UseCors
当前必须在UseResponseCaching
之前出现。UseRequestLocalization
必须在可能检查请求区域性的任何中间件(例如 app.UseStaticFiles())之前出现。- 在使用速率限制终结点特定的 API 时,必须在
UseRouting
之后调用UseRateLimiter
。 例如,如果使用 [EnableRateLimiting] 属性,则必须在 UseRouting 之后调用 UseRateLimiter。 当仅调用全局限制器时,可以在 UseRouting 之前调用 UseRateLimiter。
1 | if (env.IsDevelopment()) |
以上代码将为常见应用场景添加中间件组件:
- 异常/错误处理
- 当应用在开发环境中运行时
- 开发人员异常页中间件(
UseDevelopmentExceptionPage
)报告应用运行时错误 - 数据库错误页中间件(
UseDatabaseErrorPage
)报告数据库运行时错误
- 开发人员异常页中间件(
- 当应用在生产环境中运行时
- 异常处理程序中间件(
UseExceptionHandler
)捕获以下中间件中引发的异常 - HTTP严格传输安全协议(HSTS)中间件(
UseHsts
)添加Strict-Transport-Security标头
- 异常处理程序中间件(
- 当应用在开发环境中运行时
- HTTPS重定向中间件(
UseHttpsRedirection
)将HTTP请求重定向到HTTPS - 静态文件中间件(
UseStaticFiles
)返回静态文件,并简化进一步请求处理 - Cookie策略中间件(
UseCookiePolicy
)使应用符合欧盟一般数据保护条例(GDPR)规定 - 用于路由请求的路由中间件(
UseRouting
) - 身份验证中间件(
UseAuthentication
)尝试对用户进行身份验证,然后才会允许用户访问安全资源 - 用于授权用户访问安全资源的授权中间件(
UseAuthorization
) - 会话中间件(
UseSession
)建立和维护会话状态。如果应用使用会话状态,请在Cookie策略中间件之后和MVC中间件之前调用会话中间件 - 用于将 Razor Pages 终结点添加到请求管道的终结点路由中间件(带有
MapRazorPages
的UseEndpoints
)。
内置中间件
ASP.NET Core 附带以下中间件组件。 “顺序”列提供备注,以说明中间件在请求处理管道中的放置,以及中间件可能会终止请求处理的条件。 如果中间件让请求处理管道短路,并阻止下游中间件进一步处理请求,它被称为“终端中间件”。