## 1.Asp.Net Core Middleware(中间件)

1.1 中间件

(1)什么是中间件

  • 中间件是ASP.NET Core的核心组件,MVC框架、响应缓存、身份验证、CORS、Swagger等都是内置中间件。

中间件与容器 中间件组成_中间件

  • 广义上来讲:Tomcat、WebLogic、Redis、IIS;狭义上来讲,ASP.NET Core中的中间件指ASP.NET Core中的一个组件。
  • 中间件由前逻辑、next、后逻辑3部分组成,前逻辑为第一段要执行的逻辑代码、next为指向下一个中间件的调用、后逻辑为从下一个中间件执行返回所执行的逻辑代码。每个HTTP请求都要经历一系列中间件的处理,每个中间件对于请求进行特定的处理后,再转到下一个中间件,最终的业务逻辑代码执行完成后,响应的内容也会按照处理的相反顺序进行处理,然后形成HTTP响应报文返回给客户端。
  • 中间件组成一个管道,整个ASP.NET Core的执行过程就是HTTP请求和响应按照中间件组装的顺序在中间件之间流转的过程。开发人员可以对组成管道的中间件按照需要进行自由组合

(2)中间件的三个概念

Map、Use和Run。Map用来定义一个管道可以处理哪些请求,Use和Run用来定义管道,一个管道由若干个Use和一个Run组成,每个Use引入一个中间件,而Run是用来执行最终的核心应用逻辑。

中间件与容器 中间件组成_Core_02

(3)理解中间件执行顺序

中间件与容器 中间件组成_Core_03

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) => {
        await context.Response.WriteAsync("2  Start<br/>");
        await next.Invoke();
        await context.Response.WriteAsync("2  End<br/>");
    });
    appbuilder.Run(async ctx => {
        await ctx.Response.WriteAsync("hello middleware <br/>");
    });
});

1.2 中间件的基本实现

中间件类是一个普通的.NET类,它不需要继承任何父类或者实现任何接口,但是这个类需要有一个构造方法,构造方法至少要有一个RequestDelegate类型的参数,这个参数用来指向下一个中间件。这个类还需要定义一个名字为Invoke或InvokeAsync的方法,方法至少有一个HttpContext类型的参数,方法的返回值必须是Task类型。中间件类的构造方法和Invoke(或InvokeAsync)方法还可以定义其他参数,其他参数的值会通过依赖注入自动赋值。

中间件与容器 中间件组成_中间件_04

1.3 Markdown转html中间件

(1)安装Ude.NetStandard,获取流的编码

Install-Package Ude.NetStandard

(2)安装MarkdownSharp,md文件转html

Install-Package MarkdownSharp

(3)MarkDownViewerMiddleware

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);
    }
}

(4)app.UseMiddleware

MarkDownViewerMiddleware操作的是静态文件,所以要在UseStaticFiles之前,否则会被当做静态文件直接输出

中间件与容器 中间件组成_中间件_05

1.4 Filter和Middware的区别

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