21 | 中间件:掌控请求处理过程的关键
中间件的工作原理
ASP.NET Core 请求管道包含一系列请求委托,依次调用。
每个委托均可在下一个委托前后执行操作。 应尽早在管道中调用异常处理委托,这样它们就能捕获在管道的后期阶段发生的异常。
核心对象
- IApplicationBuilder
- RequestDelegate
- RequestDelegate就是我们处理整个请求的委托
看一下**IApplicationBuilder**
和**RequestDelegate**
的定义
namespace Microsoft.AspNetCore.Builder
{
//定义提供配置应用程序请求的机制的类管道。
public interface IApplicationBuilder
{
IServiceProvider ApplicationServices { get; set; }
IDictionary<string, object> Properties { get; }
IFeatureCollection ServerFeatures { get; }
RequestDelegate Build();
IApplicationBuilder New();
// IApplicationBuilder可以让我们去注册我们的中间件
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
}
}
IApplicationBuilder
的Use
可以让我们去注册我们的中间件,每一个委托的入参也是一个委托
RequestDelegate
这个委托的入参就是一个HttpContext
namespace Microsoft.AspNetCore.Http
{
public delegate Task RequestDelegate(HttpContext context);
}
所有注册中间件的委托实际上都是对HttpContext
的处理。
之前我们记录到过lStartup
类,Configure
方法是用来注册我们的中间件的。
根据上面流程图,发现中间件的执行顺序是跟我们的注册顺序是有关系的,
最早注册的中间件它的权力最大,可以越早发生作用。
我们不仅仅可以使用内置的中间件,也可以使用注册委托的方式来注册我们的逻辑。
新建Web
程序👉选择API
模板👉Startup
类,Configure
方法
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use(async (context, next) =>
{
// 不对next进行任何操作
await context.Response.WriteAsync("Hello World!");
});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
我们不对next
进行任何操作
执行程序:
可以发现,页面只输出了Hello World!
,如果需要后续的中间件操作,那么要在最后执行**next**
委托
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello World!");
await next();
});
执行输出:
发现页面是一片空白,这是因为,我们response
已经启动。一旦我们的应用程序已经对**response**
输出内容,我们就不能对**header**
进行操作了,但是可以在response
后续继续写出信息
我们将context.Response.WriteAsync("Hello World!")
移动到next
后面
app.Use(async (context, next) =>
{
await next();
await context.Response.WriteAsync("Hello World!");
});
执行输出: 可以发现,在Json后面添加了我们输出的HelloWorld
如果我们对response
输出内容,还继续对header
进行操作的话就会触发异常。可以通过 Context.Response.HasStarted
来判断是否进行过操作。
除了Use
这种方式之外,还有Map
方式。Map
这个函数,它的作用是我们对特殊的路径,比如hi
进行处理
app.Map("/hi", hiBuilder =>
{
hiBuilder.Use(async (context, next) =>
{
await next();
await context.Response.WriteAsync("Hello World!");
});
});
启动之后,将路径修改为hi
:
如果我们在Map的时候,逻辑复杂一点,不仅仅是判断它的URL
地址,而且还要做特殊的判断的话,可以使用MapWhen
比如说,我们请求的地址中条件包含hi
的时候
方法参数:
app.MapWhen(context => context.Request.Query.Keys.Contains("hi"), builder =>
{
builder.Run(async context => await context.Response.WriteAsync("hi!"));
});
执行输出:
这里我们用到了不同的方法Run
,**Use**
是指我们可以向注册一个完整的中间件一样,将我们的**next**
也注入进来,我们可以去决定是否执行后续的中间件。**Run**
的含义就表示我们这里就是中间件执行的末端,也就不再执行后面的中间件了。
我们如何像UseRouting
,UseEndpoints
一样来设置我们自己的中间件呢
右键项目👉新建文件夹Middlewares
👉新建类MyMiddleware
、MyBuilderExtensions
定义中间件我们使用了一个约定的方式:我们的中间件的类包含了**Invoke**
或者**InvokeAsync**
这样一个方法,入参是**HttpContext**
,返回是一个**Task**
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace LoggingSerilogDemo.Middlewares
{
class MyMiddleware
{
private readonly ILogger<MyMiddleware> _logger;
private readonly RequestDelegate _next;
public MyMiddleware(ILogger<MyMiddleware> logger, RequestDelegate next)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
_logger.LogInformation("Begin!");
await _next(context);
_logger.LogInformation("End!");
}
}
}
MyBuilderExtensions
类定义扩展方法方便直接注册
using LoggingSerilogDemo.Middlewares;
namespace Microsoft.AspNetCore.Builder
{
public static class MyBuilderExtensions
{
public static IApplicationBuilder AddMyMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<MyMiddleware>();
}
}
}
在Configure
方法里注册app.AddMyMiddleware()
执行程序:
本文作者:hiwwwk
本文链接:https://www.cnblogs.com/wwwk/p/15873401.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步