《ASP.NET Core技术内幕与项目实战》精简集-WebApi3.2:中间件Middleware
本节内容,涉及到7.6(P229-P242),以WebApi说明为主。主要NuGet包:无
一、图解中间件
1、服务器收到HTTP请求后,需要对请求进行一系列处理;当控制器中的操作方法完成后,也会对响应进行一系列处理。这一系列操作,如果全部硬编码到AspNetCore框架中,代码耦合度会很高。所以框架只完成HTTP请求响应调度和报文解析,其他可选工作由不同的中间件来完成。
2、在一个中间件中,有三部分组织,①前逻辑;②next方法;③后逻辑。HTTP请求到达服务器后,先由框架进行请求解析,然后进入第一个中间件,先执行前逻辑,然后由中间件自己决定是否执行next.Invoke(),如不执行,则请求中断,如执行,则请求进入下一个中间件。WebApi控制器的方法执行后,响应倒序从最后一个中间件开始,执行中间件的后逻辑。
3、每一次HTTP请求,框架会自动生成一个HttpContext类型的对象,称之为上下文,可以记录了请求路径、请求参数、请求客户端、响应报文、响应流、用户、自定义Items等信息。在中间件的前后逻辑和控制器中,均可以对HttpContext对象进行读写操作。
二、图解管道(pipeline)
1、上图中,每一个Use代表一个中间件,最后在Run中执行完请求,然后将响应返回。Use和Run组成一个HTTP请求的处理管道,而Map决定每个管道用于处理什么请求,Map("/test1")表示请求路径。如图所示,当服务器收到一个请求路径为"/test1"的HTTP请求时,就按管道1来处理。
2、我们用VS创建一个默认的WebApi项目时,在项目入口(Program.cs的Main方法)中,有很多“app.Use...”开头的代码,以及一个"app.Run()",这些不在Map定义的管道中的中间件,会默认处理所有请求。实际项目中,我们并不需要定义自己的Map管道,项目入口处定义的中间件,处理所有请求。也可以认为,AspNetCore默认为我们定义了一个处理所有请求的管道。
3、以下代码定义了一个处理“/test”请求的特殊管理
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); //指定处理“/test”路径请求的管道 app.Map("/test", async appBuilder => { //第一个中间件 appBuilder.Use(async (context, next) => { context.Response.ContentType = "text/html"; await context.Response.WriteAsync("1 Start<br/>"); await next.Invoke(); await context.Response.WriteAsync("1 End<br/>"); }); //第二个中间件 appBuilder.Use(async (context, next) => { context.Response.ContentType = "text/html"; await context.Response.WriteAsync("2 Start<br/>"); await next.Invoke(); await context.Response.WriteAsync("2 End<br/>"); }); //Run终点 appBuilder.Run(async ctx => { await ctx.Response.WriteAsync("hello,it is run<br/>"); }); }); app.Run();
三、AspNetCore的常见中间件
var app = builder.Build(); if (app.Environment.IsDevelopment()) { //开发环境中,捕捉异常 app.UseDeveloperExceptionPage(); //OpenApi+Swagger中间件 app.UseSwagger(); app.UseSwaggerUI(); } app.UseExceptionHandler("/Error"); //生产环境中,捕捉异常 app.UseCors(); //跨域中间件,设置响应报文头允许跨域 app.UseHttpsRedirection(); //HTTP请求重定向到HTTPS app.UseCookiePolicy(); //Cookie策略中间件 app.UseSession(); //会话中间件,建立和维护会话状态 app.UseAuthentication(); //认证,哪些用户可以登陆访问 app.UseAuthorization(); //授权,登陆用户可以访问哪些资源 app.UseFileServer(); //默认文件、静态文件、目录浏览 app.MapControllers(); //控制器,包括路由 app.Run();
四、自定义一个简单的中间件
1 //定义一个中间类 2 //CheckMiddleware.cs 3 public class CheckMiddleware 4 { 5 private readonly RequestDelegate next; 6 public CheckMiddleware(RequestDelegate next) 7 { 8 this.next = next; 9 } 10 11 public async Task InvokeAsync(HttpContext context) 12 { 13 string pwd = context.Request.Query["password"]; 14 if (pwd == "123") 15 { 16 await next(context); 17 } 18 else 19 { 20 context.Response.StatusCode = 401; 21 } 22 } 23 } 24 25 26 //使用中间件 27 //Program.cs 28 var builder = WebApplication.CreateBuilder(args); 29 ...... 30 var app = builder.Build(); 31 ...... 32 app.Map("/test", async appBuilder => 33 { 34 appBuilder.UseMiddleware<CheckMiddleware>(); 35 appBuilder.Run(async context => 36 { 37 context.Response.ContentType = "text/html"; 38 context.Response.StatusCode = 200; 39 await context.Response.WriteAsync("<h1>success!</h1>"); 40 }); 41 }); 42 app.Run();
代码解读:
3-23行:中间件类是一个普通的类,没有继承和实现。但有几个固定成员:①注入RequestDelete对象;②返回值为Task的方法InvokeAsync,且参数为HttpContext
5-9行:注入RequestDelete对象,这个对象用来指向下一个中间件,如果不调用,则请求中止。调用前为中间件的前逻辑,调用后为后逻辑。
13-17行:从HttpContext对象里,读取请求的query参数password,如果密码为123,则执行下一个中间件,否则就报401错误
32-41行:当请求路径为"/test"时,执行自定义的管道,这个管道中有一个中间件CheckMiddleware,还有一个Run的处理逻辑
补充说明:
①除了Map方法之外,还有MapGet、MapPost、MapWhen等指定管道的方法,但实际项目中,如果不是开发框架,比较少用到Map
②大部分时间,我们极少需要自己定义中间件,一般都使用框架定义好的中间件,所以对框架提供的中间件要熟悉,另外要熟悉HttpContext对象的常用成员
③书中还有两个很精彩的案例:自己动手模仿WebApi框架、Markdown转换器中间件
特别说明:
1、本系列内容主要基于杨中科老师的书籍《ASP.NET Core技术内幕与项目实战》及配套的B站视频视频教程,同时会增加极少部分的小知识点
2、本系列教程主要目的是提炼知识点,追求快准狠,以求快速复习,如果说书籍学习的效率是视频的2倍,那么“简读系列”应该做到再快3-5倍