.NET ASP.NET CORE 中间件 管道

发布时间 2023-08-28 10:54:53作者: 不争丶

一、管道

路由、认证、会话、缓存等等,他们都是通过管道来实现的
ASP.NET Core 应用一般都是使用某个框架来开发,MVC、Web API,建立在某个特殊的中间件之上。
通过编写中间件,扩展请求管道,在ASP.NET Core上创建我们自己的WEB框架,API网关Ocelot,API网关框架。

Middleware
两个职责:
1、选择是否将请求传递给管道中的下一个中间件
2、在管道中的下一个中间件的前后执行工作。

每个中间件都有权作出决定是否将请求传递给下一个中间件,也可以直接作出响应,管道的短路。

短路就是指不再将请求继续往下传递,而是结束请求并开始响应。

短路时非常有必要  

二、运用原理

1、app.Run
Run相当于一个终结点,Run之后的中间件不会被执行,因为它不像Use一样可以调用next.Invoke();
            //接收一个委托  参数:http上下文对象  这个委托就是中间件
            //Adds a terminal middleware delegate to the application's request pipeline
            //向应用程序的请求管道添加终端中间件委托 (添加是一个终端(终结点)中间件)
            app.Run(async context =>
            {
                await context.Response.WriteAsync("Hello Run");
            });

2、app.Use
传入的委托中含有一个委托参数,我们一般用next来接收这个委托参数,通过调用next.Invode()方法来调用下一个中间件,这样Use之后的中间件才能够被执行
            //Adds a terminal middleware delegate to the application's request pipeline
            //next 指向下一个中间件
            app.Use(async (context, next) =>
            {
                // 请求处理
                await context.Response.WriteAsync("Middleware 1 Begin");
                await next();
                // 响应 (返回的时候)
                await context.Response.WriteAsync("Middleware 1 End");
            });

3、app.Map
只有访问特定的路径才会执行
            app.Map("/map", app =>
            {
                app.Run(context =>
                {
                    Console.WriteLine("map-run");
                    return Task.FromResult("run done");
                });
            });


3、所谓的中间件,其实就是一堆委托

4、
            //注册自定义中间件 
            app.UseMiddleware<>(自定义的中间件类);

5、中间件的顺序,管道里的中间是有顺序的。就是我们代码的顺序
顺序是很重要

6、
            // 内置一个依赖注入框架,添加内置中间件

            // 这个中间件,他依赖一些服务类,是通过依赖注入注入进来
            // 跨域中间件 需要先注册服务 在ConfigureServices services.AddCors();
            app.UseCors();
中间件所依赖的类,都可以通过服务容器注入

7、路由中间件
            // 终端路由中间件
            //mvc 核心中间件 路由中间件
            // 识别路由,匹配路由和终结点
            // 通用的
            app.UseRouting();

            //UseEndpoints 向中间件管道添加终结点执行。 它会运行与所选终结点关联的委托。
            // 终结点中间件
            // 终结点 ,可以视为应用程序提供针对HTTP请求的处理器
            // MVC应用,终结点就对应着控制器中的某个方法
            // WebAPI 
            // 可配置的
            app.UseEndpoints(endpoints =>
            {
                //mvc 核心中间件
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

8、自定义中间件!
    // 按照约定来创建
    // 具有类型为RequestDelegate的参数的公共构造函数
    // 名为Invoke或者InvokeAsync的公共方法,这个方法必须满足两个条件:返回Task;接受类型HttpContext的第一个参数;
    public class MarkdownMiddleware
    {
        private readonly RequestDelegate next;
        private readonly IWebHostEnvironment hostEn;
        public MarkdownMiddleware(RequestDelegate next, IWebHostEnvironment hostEnv)
        {
            this.next = next;
            this.hostEn = hostEnv;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            string path = context.Request.Path.ToString();
            //只处理.md文件请求
            if (!path.EndsWith(".md", true, null))
            {
                await next.Invoke(context);
                return;
            }
            //是否存在文件流
            var file = hostEn.WebRootFileProvider.GetFileInfo(path);
            if (!file.Exists)
            {
                await next.Invoke(context);
                return;
            }

            //ude.netstandard  检测流的编码
            using var stream = file.CreateReadStream();
            Ude.CharsetDetector cdet = new Ude.CharsetDetector();
            cdet.Feed(stream);
            cdet.DataEnd();
            //探测文本编码类型
            string charset = cdet.Charset ?? "UTF-8";
            //复位,因为CharsetDetector已经把stream的指针往后挪了
            //因为后面要重新读这个流
            stream.Position = 0;
            using StreamReader reader = new StreamReader(stream, Encoding.GetEncoding(charset));

            //读取md问题
            string mdTest = await reader.ReadToEndAsync();

            Markdown md = new Markdown();
            //md-》html
            string html = md.Transform(mdTest);
            context.Request.ContentType = "test/hem;charset=UTF-8";
            await context.Response.WriteAsync(html);
        }
    }
使用 在 Startup.cs 中 Configure 添加
            //中间件 md转换器 
            app.UseMiddleware<MarkdownMiddleware>();
9、扩展方法
创建一个类扩展
    public static class CostomMiddlewareExtensions
    {
        //扩展方法
        public static IApplicationBuilder UseTest(this IApplicationBuilder app)
        {
            return app.UseMiddleware<MarkdownMiddleware>();
        }
    }
使用
            app.UseTest();
10、
你编写WEB应用,实际就是再写中间件。。全都是中间件。。
你写的控制器,也是中间件里的一部分。
整个ASP.NET Core应用,都是在管道里运行的管道里放的都是中间件

11、源码
https://github.com/dotnet/aspnetcore
Use  实际是通过委托将 _components 集合添加
(有两个USE,每个USE 都是在_components 列表里,把委托添加到进去,别的啥也没有干
没有关系,两个独立的Func委托,现在还不是真正中间件
Build  构造管道方法   
方法中:先加一个默认(底层)中间件 为了报错   
  管道就是这两句话构成的、
      挨个执行Func委托,然后把前一个中间件,塞到后一个委托中,这样遍历执行
      中间件委托就形成了一条链子,责任链模式<设计模式>:流程化
      组成了一个责任链
      最后一个被执行的,是第一个中间件,把第一个中间件返回出去了
      当我们请求进来的时候,由主机创建HTTP上下文对象,然后把上下文对象赛到第一个中间件
      这里是倒叙遍历
        for (var c = _components.Count - 1; c >= 0; c--)
        {
            app = _components[c](app);
        }
     (底层)中间件传个2号中间件的委托,包含有链子的中间件就被创建出来,把2号中间件返回出来
    1号->2号->底层->2号->1号