前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NetCore中间件实现原理

.NetCore中间件实现原理

作者头像
蓝夏
发布2021-09-09 11:44:59
4630
发布2021-09-09 11:44:59
举报
文章被收录于专栏:bluesummerbluesummer

中间件介绍

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

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

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

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

中间件配置

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

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

核心代码

代码语言:javascript
复制
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中写入以下代码

代码语言:javascript
复制
 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);
             });
 }

执行结果如下

代码语言:javascript
复制
before1
before2
hello
after2
after1

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

代码语言:javascript
复制
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);
}

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

代码语言:javascript
复制
//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呐!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-08-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 中间件介绍
  • 中间件配置
  • 核心代码
  • RequestDelegate
  • Func<RequestDelegate, RequestDelegate>
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档