zoukankan      html  css  js  c++  java
  • .NET Core开发实战(第21课:中间件:掌控请求处理过程的关键)--学习笔记(下)

    21 | 中间件:掌控请求处理过程的关键

    如果在 Map 的时候逻辑复杂一点,不仅仅判断它的 URL 地址,而且要做特殊的判断的话,可以这么做把判断逻辑变成一个委托

    我们要判断当我们的请求地址包含 abc 的时候,输出 new abc

    app.MapWhen(context =>
    {
        return context.Request.Query.Keys.Contains("abc");
    }, builder =>
    {
        builder.Run(async context =>
        {
            await context.Response.WriteAsync("new abc");
        });
    });
    

    启动程序,没有任何输出

    当我们在默认启动地址后面输入 ?abc=1 的时候,可以看到输出了 new abc

    这里用到了一个 Run 的方法,上一节用到的是 Use 方法

    app.Map("/abc", abcBuilder =>
    {
        abcBuilder.Use(async (context, next) =>
        {
            //await context.Response.WriteAsync("Hello");
            await next();
            await context.Response.WriteAsync("Hello2");
        });
    });
    

    Run 和 Use 的区别是什么呢?

    Use 是指我们可以像注册一个完整的中间件一样,将 next 注入进来,我们可以去决定是否执行后续的中间件

    Run 的含义就表示我们这里就是中间件执行的末端,也就不在执行后面的中间件了,在这里将返回请求

    那我们如何像 UseRouting UseEndpoints 一样来设计我们自己的中间件呢?

    这里定义好了一个 MyMiddleware

    namespace MiddlewareDemo.Middlewares
    {
        class MyMiddleware
        {
            RequestDelegate _next;
            ILogger _logger;
            public MyMiddleware(RequestDelegate next, ILogger<MyMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            public async Task InvokeAsync(HttpContext context)
            {
                using (_logger.BeginScope("TraceIdentifier:{TraceIdentifier}", context.TraceIdentifier))
                {
                    _logger.LogDebug("开始执行");
                    
                    await _next(context);
    
                    _logger.LogDebug("执行结束");
                }
            }
        }
    }
    

    定义中间件是用了一个约定的方式,中间件的类包含一个方法 Invoke 或者 InvokeAsync 这样一个方法,它的返回是一个 Task,入参是一个 HttpContext,实际上可以理解成与中间件的委托是一样的,只要我们的类包含这样一个方法,就可以把它作为一个中间件注册进去,并被框架识别到

    这里还定义了一个 MyBuilderExtensions

    namespace Microsoft.AspNetCore.Builder
    {
        public static class MyBuilderExtensions
        {
            public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder app)
            {
                return app.UseMiddleware<MyMiddleware>();
            }
        }
    }
    

    把我们的中间件注册进去,这个方法就是 UseMyMiddleware

    通过这样的定义,我们就可以使用自己的中间件

    app.UseMyMiddleware();
    

    启动程序,输出如下:

    控制台输出

    dbug: MiddlewareDemo.Middlewares.MyMiddleware[0]
          => RequestPath:/weatherforecast RequestId:0HLU50UEM3M9F:00000001, SpanId:|77f92fe8-4a6d800968327989., TraceId:77f92fe8-4a6d800968327989, ParentId: => TraceIdentifier:0HLU50UEM3M9F:00000001
          开始执行
    dbug: MiddlewareDemo.Middlewares.MyMiddleware[0]
          => RequestPath:/weatherforecast RequestId:0HLU50UEM3M9F:00000001, SpanId:|77f92fe8-4a6d800968327989., TraceId:77f92fe8-4a6d800968327989, ParentId: => TraceIdentifier:0HLU50UEM3M9F:00000001
          执行结束
    

    网页控制器输出

    [{"date":"2020-03-11T23:30:55.3411696+08:00","temperatureC":20,"temperatureF":67,"summary":"Warm"},{"date":"2020-03-12T23:30:55.3417863+08:00","temperatureC":52,"temperatureF":125,"summary":"Bracing"},{"date":"2020-03-13T23:30:55.3417916+08:00","temperatureC":-3,"temperatureF":27,"summary":"Mild"},{"date":"2020-03-14T23:30:55.341792+08:00","temperatureC":35,"temperatureF":94,"summary":"Balmy"},{"date":"2020-03-15T23:30:55.3417923+08:00","temperatureC":37,"temperatureF":98,"summary":"Sweltering"}]Hello2
    

    如果要实现一个断路器,就是不执行后续逻辑,注释掉一行

    _logger.LogDebug("开始执行");
    
    //await _next(context);
    
    _logger.LogDebug("执行结束");
    

    启动程序,页面不会输出任何内容,只会在控制台打印出中间件的执行过程,后续的控制器不会执行

    这样就实现了一个断路器,也就意味着可以使用自己的中间件做请求的控制,而且时非常灵活的控制

    在使用中间件的过程中,需要非常注意的是注册中间件的顺序,这些顺序就决定了中间件执行的时机,某些中间件会是断路器的作用,某些中间件会做一些请求内容的处理

    还有一个比较关键的要点是指应用程序一旦开始向 Response write 的时候,后续的中间件就不能再去操作它的 header,这一点是需要注意的

    可以通过 Context.Response.HasStarted 来判断是否已经开始向响应的 body 输出内容,一旦输出了内容,就不要再操作 header

    知识共享许可协议

    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

    欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

    如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

  • 相关阅读:
    C语言 · 字符串输入输出函数
    C语言 · 判定字符位置
    剑指offer二之替换空格
    剑指offer一之二维数组中的查找
    算法复杂度:时间复杂度与空间复杂度
    二分查找法
    信息熵
    K-means算法的原理、优缺点及改进(转)
    office2013安装与卸载
    Deep Learning (中文版&英文版)
  • 原文地址:https://www.cnblogs.com/MingsonZheng/p/12459751.html
Copyright © 2011-2022 走看看