zoukankan      html  css  js  c++  java
  • .NetCore中间件实现原理

    中间件介绍

    中间件是在应用程序管道处理请求和响应的一个链

    每个组件都可以在请求处理前后做一些操作,并决定是否将请求交给下一个组件处理

    如果一个中间件没有把请求交给下一个中间件,称之为管道短路

    中间件的默认实现类在 Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder 中

    中间件配置

    配置中间件的方式很多,包括UseMiddlewareUseRun等等

    但大部分配置方式都是扩展方法,最终调用的函数只有 Use(Func<RequestDelegate, RequestDelegate> middleware)

    核心代码

    public delegate Task RequestDelegate(HttpContext context);
    
    private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
    
    public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
    {
        _components.Add(middleware);
        return this;
    }
    
    public RequestDelegate Build()
    {
        RequestDelegate requestDelegate = delegate(HttpContext context)
        {
            context.Response.StatusCode = 404;
            return Task.CompletedTask;
        };
        foreach (Func<RequestDelegate, RequestDelegate> item in _components.Reverse())
        {
            requestDelegate = item(requestDelegate);
        }
        return requestDelegate;
    }
    

    这是ApplicationBuilder中的核心代码,一眼看上去很简单。但是这么这么多层的委托嵌套难以阅读,我们接下来将他们拆开来看

    RequestDelegate

    这个委托不用多说,参数为HttpContext上下文,返回一个Task

    Func<RequestDelegate, RequestDelegate>

    这个就有意思了,他的入参是Next,返回值是当前要执行的委托,不要理解反了。

    我们在Startup的Configure中写入以下代码

     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
     {
         app.Use((RequestDelegate next) => async (HttpContext context) =>
                 {
                     Console.WriteLine("before1");
                     await next.Invoke(context);
                     Console.WriteLine("after1");
                 });
    
         app.Use((RequestDelegate next) => async (HttpContext context) =>
                 {
                     Console.WriteLine("before2");
                     await next.Invoke(context);
                     Console.WriteLine("after2");
                 });
         app.Use((RequestDelegate next) => async (HttpContext context) =>
                 {
                     if (context.Request.Path == "/hello")
                     {
                         context.Response.StatusCode = 200;
                         await context.Response.WriteAsync("hello");
                         return;
                     }
                     await next.Invoke(context);
                 });
     }
    

    执行结果如下

    before1
    before2
    hello
    after2
    after1
    

    如果仍然难以看懂,那我们继续拆分

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        Func<RequestDelegate, RequestDelegate> func1 = (RequestDelegate next) =>
        {
            return async context =>
            {
                {
                    Console.WriteLine("before1");
                    await next.Invoke(context);
                    Console.WriteLine("after1");
                }
            };
        };
    
        Func<RequestDelegate, RequestDelegate> func2 = (RequestDelegate next) =>
        {
            return async context =>
            {
                {
                    Console.WriteLine("before2");
                    await next.Invoke(context);
                    Console.WriteLine("after2");
                }
            };
        };
    
        Func<RequestDelegate, RequestDelegate> func3 = (RequestDelegate next) =>
        {
            return async context =>
            {
                {
                    if (context.Request.Path == "/hello")
                    {
                        Console.WriteLine("hello");
                        context.Response.StatusCode = 200;
                        await context.Response.WriteAsync("hello");
                        return;
                    }
                    await next.Invoke(context);
                }
            };
        };
    
        app.Use(func1);
        app.Use(func2);
        app.Use(func3);
    }
    

    接下来我们看一下中间件的构建过程,来解释为什么会是上面的输出结果

    //Use()函数负责向_components列表中插入委托,此时_components中存在的委托顺序为 func1,func2,func3
    
    //接下来我们主要看Build()函数
    
    //1.这是build函数中定义的最后一个短路委托,不会再向下调用
    RequestDelegate requestDelegate = delegate(HttpContext context)
    {
        context.Response.StatusCode = 404;
        return Task.CompletedTask;
    };
    
    //2._components.Reverse()将注入的委托顺序反转,执行循环
    // 反转后的顺序变成了 func3 ,func2, func1
    // 第一次1循环得到结果: requestDelegate=func3(requestDelegate)
    // 第一次2循环得到结果: requestDelegate=func2(func3(requestDelegate))
    // 第一次3循环得到结果: requestDelegate=func1(func2(func3(requestDelegate)))
    
    //3. 结合我们的代码,最终执行顺序如下
    
    requestDelegate = async (context) =>
                 {
                     Console.WriteLine("before1");
                         Console.WriteLine("before2");
                             if (context.Request.Path == "/hello")
                             {
                                 Console.WriteLine("hello");
                                 context.Response.StatusCode = 200;
                                 await context.Response.WriteAsync("hello");
                                 return;
                             }
                                 context.Response.StatusCode = 404;
                         Console.WriteLine("after2");
                     Console.WriteLine("after1");
                 };
    
    //4.最终将requestDelegate返回
    

    所以在接收到请求时,中间件的处理顺序就会按照我们定义的顺序来执行啦,经过上面的介绍,是不是感觉So Easy呐!

  • 相关阅读:
    day 29 什么是元类、class底层原理分析、通过元类来控制类的产生、通过元类控制类的调用过程、有了元类之后的属性查找
    day 28 断点调试,反射,内置方法
    day 26 绑定方法,非绑定方法,面向对象串讲
    day 25 类的组合、多太与多态性、封装
    day 24 类的继承
    函数进阶(1)
    函数基础
    文件修改及函数定义
    文件处理
    字典类型内置方法
  • 原文地址:https://www.cnblogs.com/bluesummer/p/15203899.html
Copyright © 2011-2022 走看看