.NET 中的中间件(Middleware)
ASP.NET Core 中间件
什么是中间件(Middleware)?
中间件是组装到应用程序管道中以处理请求和响应的软件。 每个组件:
- 选择是否将请求传递给管道中的下一个组件。
- 可以在调用管道中的下一个组件之前和之后执行工作。
请求委托(Request delegates)用于构建请求管道,处理每个HTTP请求。
请求委托使用Run,Map和Use扩展方法进行配置。单独的请求委托可以以内联匿名方法(称为内联中间件)指定,或者可以在可重用的类中定义它。这些可重用的类和内联匿名方法是中间件或中间件组件。请求流程中的每个中间件组件都负责调用流水线中的下一个组件,如果适当,则负责链接短路。
将HTTP模块迁移到中间件解释了ASP.NET Core和以前版本(ASP.NET)中的请求管道之间的区别,并提供了更多的中间件示例。
使用 IApplicationBuilder 创建中间件管道
ASP.NET Core请求流程由一系列请求委托组成,如下图所示(执行流程遵循黑色箭头):
每个委托可以在下一个委托之前和之后执行操作。委托还可以决定不将请求传递给下一个委托,这称为请求管道的短路。短路通常是可取的,因为它避免了不必要的工作。例如,静态文件中间件可以返回一个静态文件的请求,并使管道的其余部分短路。需要在管道早期调用异常处理委托,因此它们可以捕获后面管道的异常。
最简单的可能是ASP.NET Core应用程序建立一个请求的委托,处理所有的请求。此案例不包含实际的请求管道。相反,针对每个HTTP请求都调用一个匿名方法。
第一个 app.Run 委托终止管道。
可以看出请求委托的执行顺序是遵循上面的流程图的。
Use, Run, 和 Map
你可以使用Use,Run和Map配置HTTP管道。
- Use方法可以使管道短路(即,可以不调用下一个请求委托)。
- Run方法是一个约定, 并且一些中间件组件可能暴露在管道末端运行的Run [Middleware]方法。
- Map*扩展用作分支管道的约定。映射根据给定的请求路径的匹配来分支请求流水线,如果请求路径以给定路径开始,则执行分支。
内置中间件
中间件 | 作用 |
---|---|
Authentication 提供身份验证支持 | |
CORS | 配置跨域资源共享 |
Response | Caching 提供缓存响应支持 |
Response | Compression 提供响应压缩支持 |
Routing | 定义和约束请求路由 |
Session | 提供用户会话管理 |
Static Files | 为静态文件和目录浏览提供服务提供支持 |
URL Rewriting Middleware | 用于重写 Url,并将请求重定向的支持 |
自定义中间件
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline
return this._next(context);
}
以下通过IApplicationBuilder的扩展方法暴露中间件:
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
以下代码从Configure调用中间件:
app.UseRequestCulture();
每个请求的依赖关系
因为中间件是在应用程序启动时构建的,而不是每个请求,所以在每个请求期间,中间件构造函数使用的作用域生命周期服务不会与其他依赖注入类型共享。 如果您必须在中间件和其他类型之间共享作用域服务,请将这些服务添加到Invoke方法的签名中。 Invoke方法可以接受由依赖注入填充的其他参数。 例如:
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty = 1000;
await _next(httpContext);
}
}