本篇已收录至 asp.net core 随笔系列
Asp.Net Core那点事儿 - 玩转中间件配置Http管道
目的是了解:
极简模式的理解
由于中间件的存在, asp.net core 的框架后端代码可以只是一些必要的MVC code或者 api code, 项目中需要什么就添加什么类型的中间件. 真正意义上实现了 pay what you really need. 这与原来的 asp.net 有显著的区别. asp.net web app 构建出来后, 始终包含了所有内置的功能. 即使你的项目并不需要这些功能, 但是还是会有. 所以在这一点的区别上, 也不难看出, asp.net core 构建的 web 的性能要比 asp.net 构建的 web 好很多.
middleware相关概念
- 一般来说 asp.net core 都是在 startup.cs 文件的 configure 方法中配置 http 管道模型. 并且 http 管道模型是由多个 middleware 组成的.
- vs创建的 startup.cs 文件使用的是默认的构造函数, 如果想重载构造函数, 构造函数支持的参数只能是这三个当中选择: IWebHostEnvironment, IHostEnvironment, IConfiguration. 其他的 service 是不可用的, 直到 Configure 方法调用完毕.
- 可以配置多个 startup文件, 比如 startupDevelopment.cs, 当环境mode匹配时, 被匹配到的 startup 文件的优先级最高.
- ConfigureService 方法是配置额外的 service 注册进入容器, 为了应用程序后续使用的.
- Configure 方法是配置响应 http request 管道的方法. 其实是将各种中间件组件添加至 IApplicationBuilder 实例的 Components 集合中完成的.
终端中间件
请求管道中的每个中间件组件负责调用管道中的下一个组件,或使管道短路。 当中间件短路时,它被称为“终端中间件” ,因为它阻止中间件进一步处理请求。
常用中间件列举
- Exception middleware:
- [UseDeveloperExceptionPage]
- [UseExceptionHandler]
- [UseHttpsRedirection] HTTPS 重定向中间件 将 HTTP 请求重定向到 HTTPs
- [UseStaticFiles] 返回静态文件,并简化进一步请求处理
- [UseCookiePolicy]
- [UseRouting]
- [UseAuthentication] 身份验证不使未经身份验证的请求短路。 虽然身份验证中间件对请求进行身份验证,但仅在 MVC 选择特定 Razor 页或 MVC 控制器和操作后,才发生授权(和拒绝)
- [UseAuthorization]
- [UseSession] 需要在 cookie 和 mvc 中间件之间使用
- [UseEndpoints]
使用中间件配置Http管道
IApplicationBuilder
Configure方法主要是使用 IApplicationBuilder 的实现类 ApplicationBuilder 的实例来配置 http 管道. 即调用 use, map等配置管道的function们. ApplicationBuilder源码:
重点是 StartAsync 这个方法, 是在 HostBuilder.Build 出 Internal.Host 实例以后, 执行 Run() 方法后, 从 Host 的 StartAsync 方法里面从容器中获取注册的 HostedService 的实例调用的.
Use
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_components.Add(middleware);
return this;
}
Run
Map
用作约定来创建管道分支
public class Startup
{
private static void HandleMapTest1(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 1");
});
}
private static void HandleMapTest2(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Map Test 2");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map("/map1", HandleMapTest1);
app.Map("/map2", HandleMapTest2);
app.Map("/level1", level1App => {
level1App.Map("/level2a", level2AApp => { // "/level1/level2a" processing });
level1App.Map("/level2b", level2BApp => { // "/level1/level2b" processing });
});
app.Map("/map1/seg1", HandleMultiSeg);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}
MapWhen
基于给定谓词的结果创建请求管道分支
public class Startup
{
private static void HandleBranch(IApplicationBuilder app)
{
app.Run(async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
public void Configure(IApplicationBuilder app)
{
app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
HandleBranch);
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}
中间件详细介绍
中间件结构介绍
在底层提供的中间件项目中有N多个中间件, 如图:
基本上一个中间件至少要包含三个文件: xxxxExtension.cs, xxxxMiddleware.cs, xxxxOptions.cs
xxxxExtension.cs
用于提供 Use function, 实际是 ApplicationBuilder 的扩展方法:
支持了三个方法, 一个是默认不带参数的, 一个是带参数的. 带参数的将参数放到 xxxxOptions里面, 再继续调用 app.UseMiddleware()
底层会根据传入的 type 创建一个实例, 是 xxxxMiddleware 的实例.
xxxxMiddleware.cs
中间件的实际业务类, 基本像这样实现:
自己动手写一个中间件
public class TestMiddleware
{
private readonly RequestDelegate _next;
private static IParaInterface _parameter;
public TestMiddleware(RequestDelegate next, IParaInterface parameter)
{
_next = next;
_parameter = parameter;
}
public async Task Invoke(HttpContext context)
{
// Your code
_parameter.someFunction();
await _next.Invoke(context);
}
}
interface
public interface IParaInterface
{
void someFunction();
}
service
public class ParaClass : IParaInterface
{
public void someFunction()
{
}
}
extension
public static class TestMiddlewareExtensions
{
public static IApplicationBuilder UseTestMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<TestMiddleware>();
}
}
注入 service:
services.AddTransient<IParaInterface, ParaClass>();
最后使用这个中间件:
app.UseTestMiddleware();