中间件
概念
我怕我解释不清楚,这里直接上课件



基本使用
我们建立一个空的asp.net core项目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | public static void Main(string[] args) {             var builder = WebApplication.CreateBuilder(args);             var app = builder.Build();
              //app.MapGet("/", () => "Hello World!");             app.Map("/test", async (pipeBuilder) => {                 pipeBuilder.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/>");                 });                 pipeBuilder.Use(async (context, next) => {                     await context.Response.WriteAsync("2 Start <br/>");                     await next.Invoke();                     await context.Response.WriteAsync("2 End <br/>");                 });                 pipeBuilder.Run(async (context) => {                     await context.Response.WriteAsync("Run <br/>");                 });             });
              app.Run();         }
  | 
 
以上仅为了演示,一般都在Run中输出内容,如果在Use中输出了内容,就不要next了,不然容易引起混乱。
简单的自定义中间件
1、如果中间件的代码比较复杂,或者我们需要重复使用一个中间件的话,我们最好把中间件的代码放到一个单独的“中间件类”中。
2、中间件类是一个普通的类,不需要继承任何父类或者实现任何接口,但是这个类需要有一个构造方法,构造方法至少要有一个RequestDelegate类型的参数(就是上面的next),这个参数用来指向下一个中间件。这个类还需要定义一个名字为Invoke或者InvokeAsync的方法,方法至少有一个HttpContext类型的参数,方法的返回值必须是Task类型。中间件类的构造方法和Invoke(或者InvokeAsync)方法还可以定义其他参数,其他参数的值会通过依赖注入自动赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | //自定义中间件类 public class CheckAndParsingMiddleware {         private readonly RequestDelegate next;         public CheckAndParsingMiddleware(RequestDelegate next) {             this.next = next;         }         public async Task InvokeAsync(HttpContext context) {             string password = context.Request.Query["password"];             if(password == "123") {                 if (context.Request.HasJsonContentType()) {                     //如果请求中有json就往下执行(浏览器看不出来,去postman上看)
                      //如果password为123就将请求报文转为dynamic类型                     var reqStream = context.Request.BodyReader.AsStream();//转成流的格式                     //需要安装Dynamic.json包                     //目前(.net6)System.Text.Json不支持把json反序列化为dynamic类型                     dynamic? jsonObj = DJson.Parse(reqStream);                     context.Items["BodyJson"] = jsonObj;                 }                 await next.Invoke(context);             } else {                 context.Response.StatusCode = 401;             }         }     }
   | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | //Program.cs app.Map("/test", async (pipeBuilder) => {                 pipeBuilder.UseMiddleware<CheckAndParsingMiddleware>();                 pipeBuilder.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/>");                 });                 pipeBuilder.Run(async (context) => {                     await context.Response.WriteAsync("Run <br/>");                     //context.Item可以在同一个请求中跨中间件进行消息传递                     dynamic obj = context.Items["BodyJson"];                     if(obj != null) {                         await context.Response.WriteAsync($"{obj.ToString()}<br/>");                     }                      });             });
   | 
 
案例(Markdown渲染中间件)
Markdown不被浏览器支持,所以编写一个在服务器端把Markdown转换为HTML的中间件。
我们开发的中间件是构件在ASP.NET Core内置的StaticFiles中间件之上,并且在它之前运行,所有的*.md文件都被放到wwwroot文件夹下,当我们请求wwwroot下其他静态文件时,StaticFiles中间件会把它们返回给浏览器,而当我们请求wwwroot下的*.md文件时,我们编写的中间件会读取对应的*.md文件,并把它们转换为HTML格式返回给浏览器
检测文本编码:
安装Ude.NetStandard包。
通过CharsetDetector类探测文件编码。
Markdown->HTML:
安装MarkdownSharp包。
通过Markdown实例的Transform()方法将md文本转换为html。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
   | using MarkdownSharp; using Microsoft.Extensions.Caching.Memory; using System.Text; using Ude;
  public class MarkDownViewerMiddleware {     private readonly RequestDelegate next;     private readonly IWebHostEnvironment hostEnv;     private readonly IMemoryCache memCache;
      public MarkDownViewerMiddleware(RequestDelegate next,         IWebHostEnvironment hostEnv, IMemoryCache memCache) {         this.next = next;         this.hostEnv = hostEnv;         this.memCache = memCache;     }
      /// <summary>     /// 检测流的编码     /// </summary>     /// <param name="stream"></param>     /// <returns></returns>     private static string DetectCharset(Stream stream) {         CharsetDetector charDetector = new();         charDetector.Feed(stream);         charDetector.DataEnd();         string charset = charDetector.Charset ?? "UTF-8";         stream.Position = 0;         return charset;     }
      /// <summary>     /// 读取文本内容     /// </summary>     /// <param name="stream"></param>     /// <returns></returns>     private static async Task<string> ReadText(Stream stream) {         string charset = DetectCharset(stream);         using var reader = new StreamReader(stream, Encoding.GetEncoding(charset));         return await reader.ReadToEndAsync();     }
      public async Task InvokeAsync(HttpContext context) {         string path = context.Request.Path.Value ?? "";         if (!path.EndsWith(".md")) {             await next(context);             return;         }         var file = hostEnv.WebRootFileProvider.GetFileInfo(path);         if (!file.Exists) {             await next(context);             return;         }         context.Response.ContentType = $"text/html;charset=UTF-8";         context.Response.StatusCode = 200;         string cacheKey = nameof(MarkDownViewerMiddleware)             + path + file.LastModified;         var html = await memCache.GetOrCreateAsync(cacheKey, async ce => {             ce.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);             using var stream = file.CreateReadStream();             string text = await ReadText(stream);             Markdown markdown = new Markdown();             return markdown.Transform(text);         });         await context.Response.WriteAsync(html);     } }
   | 
 
1 2 3 4 5
   | //Program.cs //注意中间件的位置             app.UseHttpsRedirection();             app.UseMiddleware<MarkDownViewerMiddleware>();             app.UseStaticFiles();
   | 
 
中间件和Filter的区别

区别:
1、中间件可以处理所有的请求,而Filter只能处理对控制器的请求;中间件运行在一个更底层、更抽象的级别,因此在中间件中无法处理MVC中间件特有的概念。
2、中间件和Filter可以完成很多相似的功能。“未处理异常中间件”和“未处理异常Filter”;“请求限流中间件”和“请求限流FIlter”
3、优先选择使用中间件;但是如果这个组件只针对MVC或者需要调用一些MVC相关的类的时候,我们就只能选择Filter