前言
本质上,中间件类也是一个普通的 .NET 类,它不需要继承任何父类或者实现任何接口。
但是有几个约定:
- 需要有一个构造方法
- 构造方法至少要有一个 RequestDelegate 类型的参数,用来指向下一个中间件。
- 需要定义一个名字为 Invoke 或 InvokeAsync 的方法
- 此方法中至少有一个 HttpContext 类型的参数
- 此个方法的返回值必须是 Task 类型
只要遵守以上这些约定,就可以轻松创建自己的中间件。
以下是一个 Step By Step 例子。
Step By Step 步骤
-
创建一个空 ASP.NET Core webapi 项目
-
定义一个用于中间件的实体类
public class CheckAndParsingMiddlewareModel { public int i { get; set; } public int j { get; set; } }
-
定义一个中间件类 CheckAndParsingMiddleware,留意注释
using System.Text.Json; using System.Text; /// <summary> /// 自定义中间类,要求如注释的 1~5 点 /// </summary> public class CheckAndParsingMiddleware { private readonly RequestDelegate next; // 1. 需要有一个构造方法 // 2. 构造方法需要有一个 RequestDelegate 类型的参数,用于指向下一个中间件 public CheckAndParsingMiddleware(RequestDelegate next) { this.next = next; } // 3. 需要有一个名为 Invoke 或 InvokeAsync 的方法 // 4. 此方法至少要有一个 HttpContext 类型参数 // 5. 此方法的返回值必须是 Task 类型 public async Task InvokeAsync(HttpContext context) { string pwd = context.Request.Query["password"]; if (pwd == "123") { if (context.Request.HasJsonContentType()) { // 解析 Body 参数的值 var reqStream = context.Request.BodyReader.AsStream(); var jsonObj = JsonSerializer.Deserialize<CheckAndParsingMiddlewareModel>(reqStream); // 将数据存在Items中 // HttpContext.Items 在同一个请求中是共享的,可以用它在多个中间件之间来传递数据 context.Items["BodyJson"] = jsonObj; } // 把请求转给下一个中间件 await next(context); } else { context.Response.StatusCode = 401; } } }
-
打开 Program.cs,编写使用自定义的中间件的代码,如:
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.Map("/testForCustomMiddleware", async appbuild => { // 使用自定义的中间件 appbuild.UseMiddleware<CheckAndParsingMiddleware>(); // 执行 appbuild.Run(async ctx => { ctx.Response.ContentType = "text/html"; ctx.Response.StatusCode = 200; var jsonObj = (CheckAndParsingMiddlewareModel)ctx.Items["BodyJson"]; int i = jsonObj.i; int j = jsonObj.j; await ctx.Response.WriteAsync($"{i}+{j}={i+j}"); }); }); app.Run();
-
其它:解析 Body 参数值的方法
// 方法 1 var reqStream = context.Request.BodyReader.AsStream(); var jsonObj = JsonSerializer.Deserialize<CheckAndParsingMiddlewareModel>(reqStream); // 方法 2 Stream reqStream = context.Request.Body; byte[] buffer = new byte[context.Request.ContentLength!.Value]; await reqStream.ReadAsync(buffer, 0, buffer.Length); var reqStr = Encoding.UTF8.GetString(buffer); dynamic jsonObj = JsonSerializer.Deserialize<dynamic>(reqStr)!;
扩展
中间件类的构造方法和 Invoke(或 InvokeAsync)方法还可以定义其他参数,其他参数会通过依赖注入自动赋值。