管道模型
ASP.NET管道
当一个http请求被送入到HttpRuntime之后,这个Http请求会继续被送入到一个被称之为HttpApplication Factory的一个容器当中,而这个容器会给出一个HttpApplication实例来处理传递进来的http请求,而后这个Http请求会依次进入到如下几个容器中:HttpModule --> HttpHandler Factory --> HttpHandler。当系统内部的HttpHandler的ProcessRequest方法处理完毕之后,整个Http Request就被处理完成
ASP.NET Core管道
.NET Core管道支持热插拔,用一个个中间件(Middleware)取代ASP.NET的IHttpModule 和IHttpHandler
- Server将接收到的请求直接向后传递,依次经过每一个中间件进行处理,然后由最后一个中间件处理并生成响应内容后回传,再反向以此经过每个中间件,直到由Server发送出去
- 中间件就像一层一层的“滤网”,过滤所有的请求和响应
名词浅析
管道:决定如何处理请求,由Startup.cs文件中的Configure()进行配置
// .NET Core3.1默认代码
// 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.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
中间件(Middleware):是一个可以处理HTTP请求或响应的软件管道
- 中间件组件可以处理请求, 并决定不调用管道中的下一个中间件,从而使管道短路
- 可以同时传入请求和传出响应
- 中间件组件是按照添加到管道的顺序进行执行的
中间件
注册
- 终结者模式(Run())
- Run 终结式只是执行,没有去调用Next ,一般作为终结点。
- 所谓Run终结式注册,其实只是一个扩展方法,最终还是调用Use方法
- Use 方法注册
- use 方式注册中间件得出的结论是:Use注册动作 不是终结点 ,执行next,就可以执行下一个中间件
- 如果不执行,就等于Run
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello 1 开始");
await next();//调用下一个中间件
await context.Response.WriteAsync("Hello 1 结束");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello 2 开始");
await next();
});
Hello 1 开始
Hello 2 开始
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
_components.Add(middleware);
return this;
}
public RequestDelegate Build()
{
RequestDelegate app = context =>
{
// If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
// This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
var endpoint = context.GetEndpoint();
var endpointRequestDelegate = endpoint?.RequestDelegate;
if (endpointRequestDelegate != null)
{
var message =
$"The request reached the end of the pipeline without executing the endpoint: '{endpoint.DisplayName}'. " +
$"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " +
$"routing.";
throw new InvalidOperationException(message);
}
context.Response.StatusCode = 404;
return Task.CompletedTask;
};
foreach (var component in _components.Reverse())
{
app = component(app);
}
return app;
}
- IApplicationBuilder build之后其实就是一个RequestDelegate,能对HttpContext加以处理,默认情况下,管道是空的,就是404;
- 可以根据需求进行任意的配置,一切全部由开发者自由定制,框架只是提供了一个组装方式
封装中间件
根据源码,可以改进为:
public class CustomMiddleWare
{
private readonly RequestDelegate _next;
//依赖注入
public CustomMiddleWare(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync($"{nameof(CustomMiddleWare)},Hello World1!<br/>");
await _next(context);
await context.Response.WriteAsync($"{nameof(CustomMiddleWare)},Hello World2!<br/>");
}
}
注册使用:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<CustomMiddleWare>();
}
进一步封装
使用扩展方法,将这个类中的逻辑作为IApplicationBuilder的扩展方法
public static class MiddleExtend
{
public static IApplicationBuilder UseCustomMiddleWare(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleWare>();
}
}
注册使用:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCustomMiddleWare();
}