zoukankan      html  css  js  c++  java
  • 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)【有源码】

    原文:循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)

    系列目录

    循序渐进学.Net Core Web Api开发系列目录

     本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi

    一、概述

    本篇介绍如何使用中间件(Middleware)。

    二、初步演练

    先写几个中间件

    复制代码
    public class DemoAMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger _logger;
    
        </span><span style="color: #0000ff;">public</span> DemoAMiddleware(RequestDelegate next, ILogger&lt;DemoAMiddleware&gt;<span style="color: #000000;"> logger)
        {
            _next </span>=<span style="color: #000000;"> next;
            _logger </span>=<span style="color: #000000;"> logger;
        }
    
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">async</span><span style="color: #000000;"> Task Invoke(HttpContext context)
        {
            _logger.LogInformation(</span><span style="color: #800000;">"</span><span style="color: #800000;">(1) DemoAMiddleware.Invoke front</span><span style="color: #800000;">"</span><span style="color: #000000;">);  
            </span><span style="color: #0000ff;">await</span><span style="color: #000000;"> _next(context);
            _logger.LogInformation(</span><span style="color: #800000;">"</span><span style="color: #800000;">[1] DemoAMiddleware:Invoke back</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        }       </span><span style="color: #000000;">
    }
    

    public class DemoBMiddleware
    {
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

        </span><span style="color: #0000ff;">public</span> DemoBMiddleware(RequestDelegate next, ILogger&lt;DemoBMiddleware&gt;<span style="color: #000000;"> logger)
        {
            _next </span>=<span style="color: #000000;"> next;
            _logger </span>=<span style="color: #000000;"> logger;
        }
    
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">async</span><span style="color: #000000;"> Task Invoke(HttpContext context)
        {
    
            _logger.LogInformation(</span><span style="color: #800000;">"</span><span style="color: #800000;">(2) DemoBMiddleware.Invoke part1</span><span style="color: #800000;">"</span><span style="color: #000000;">);  
            </span><span style="color: #0000ff;">await</span><span style="color: #000000;"> _next(context);
            _logger.LogInformation(</span><span style="color: #800000;">"</span><span style="color: #800000;">[2] DemoBMiddleware:Invoke part2</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        }      </span><span style="color: #000000;">
    }
    
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> RequestRecordMiddleware
    {
        </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">readonly</span><span style="color: #000000;"> RequestDelegate _next;
        </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">readonly</span><span style="color: #000000;"> ILogger _logger;
    
        </span><span style="color: #0000ff;">public</span> RequestRecordMiddleware(RequestDelegate next, ILogger&lt;RequestRecordMiddleware&gt;<span style="color: #000000;"> logger)
        {
            _next </span>=<span style="color: #000000;"> next;
            _logger </span>=<span style="color: #000000;"> logger;
        }
    
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">async</span><span style="color: #000000;"> Task Invoke(HttpContext context)
        {
            _logger.LogInformation(</span><span style="color: #800000;">"</span><span style="color: #800000;">(3) RequestRecordMiddleware.Invoke</span><span style="color: #800000;">"</span><span style="color: #000000;">);
    
            String URL </span>=<span style="color: #000000;"> context.Request.Path.ToString();
            _logger.LogInformation($</span><span style="color: #800000;">"</span><span style="color: #800000;">URL : {URL}</span><span style="color: #800000;">"</span><span style="color: #000000;">);            
    
            </span><span style="color: #0000ff;">await</span><span style="color: #000000;"> _next(context);
    
            _logger.LogInformation(</span><span style="color: #800000;">"</span><span style="color: #800000;">[3] RequestRecordMiddleware:Invoke next</span><span style="color: #800000;">"</span><span style="color: #000000;">);
            _logger.LogInformation($</span><span style="color: #800000;">"</span><span style="color: #800000;">StatusCode : {context.Response.StatusCode}</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        }       </span><span style="color: #000000;">
    }</span></pre>
    
    复制代码

    以上中间件前两个没有做什么正经工作,就打印一些日志信息,第三个干了一点工作,它打印了用户输入的url,同时打印了返回给客户端的状态码。

    要使中间件工作,需要启用中间件。

    复制代码
       public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
        </span><span style="color: #0000ff;">public</span> IConfiguration Configuration { <span style="color: #0000ff;">get</span><span style="color: #000000;">; }
    
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> This method gets called by the runtime. Use this method to add services to the container.</span>
        <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> ConfigureServices(IServiceCollection services)
        {            
            services.AddMvc();</span><span style="color: #000000;">
        }
    
        </span><span style="color: #008000;">//</span><span style="color: #008000;"> This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span>
        <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseUnifyException();
    
            <span style="color: #ff00ff;">app.UseMiddleware</span></span><span style="color: #ff00ff;">&lt;DemoAMiddleware&gt;();
            app.UseMiddleware&lt;DemoBMiddleware&gt;();           
            app.UseMiddleware&lt;RequestRecordMiddleware&gt;</span><span style="color: #000000;"><span style="color: #ff00ff;">();    </span>        
    
            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }</span></pre>
    
    复制代码

    通过扩展方法,我们对中间件的启用代码进行改造:

    复制代码
    public static class RequestRecordMiddlewareExtensions
        {
            public static IApplicationBuilder UseRequestRecord(this IApplicationBuilder builder)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException("builder is null");
                }
    
            </span><span style="color: #0000ff;">return</span> builder.UseMiddleware&lt;RequestRecordMiddleware&gt;<span style="color: #000000;">();
        }
    }</span></pre>
    
    复制代码

    此时启用代码由:app.UseMiddleware<RequestRecordMiddleware>(); 

    可以修改为:   app.UseRequestRecord();

    实现效果没有变化。可见下面代码都是中间件的使用。

    1
    2
    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();

      

    我的理解,中间件类似车间流水线上的工人,操作的零件就是HttpContext,每个人负责两道工序,我把它们定义为“前道工序”和“后道工序”,通过代码 _next(context); 把两道工序隔离开,处理的顺序需要特别注意,按照中间件注册的顺序依次处理“前道工序”,处理完成后,再按相反的顺序处理“后道工序”,如果某个中间件没有调用_next(context),那么就不会调用后续的中间件,所以中间件启用的顺序是需要特别考虑的。

    以上代码中三个中间件输出到控制台的信息顺序如下:

    (1)

    (2)

    (3)

    【3】

    【2】

    【1】

    个人认为,“前道工序”应重点处理Request,“后道工序”应重点处理Response。

    三、做一个类似MVC的中间件

    我们做一个中间件,让其返回一些内容给客户端:

    复制代码
    public class MyMvcMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger _logger;
    
        </span><span style="color: #0000ff;">public</span> MyMvcMiddleware(RequestDelegate next, ILogger&lt;DemoAMiddleware&gt;<span style="color: #000000;"> logger)
        {
            _next </span>=<span style="color: #000000;"> next;
            _logger </span>=<span style="color: #000000;"> logger;
        }
    
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">async</span><span style="color: #000000;"> Task Invoke(HttpContext context)
        {  </span><span style="color: #0000ff;">var</span> str = <span style="color: #800000;">"</span><span style="color: #800000;">hello,world!</span><span style="color: #800000;">"</span><span style="color: #000000;">;
            </span><span style="color: #0000ff;">await</span><span style="color: #000000;">  context.Response.WriteAsync(str);          
        }       
    }</span></pre>
    
    复制代码

    这个中间件只是返回固定的字符串,我们还可以调用某个Controller的提供的方法。

    复制代码
       public class MyMvcMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger _logger;
    
        </span><span style="color: #0000ff;">public</span> MyMvcMiddleware(RequestDelegate next, ILogger&lt;DemoAMiddleware&gt;<span style="color: #000000;"> logger)
        {
            _next </span>=<span style="color: #000000;"> next;
            _logger </span>=<span style="color: #000000;"> logger;
        }
    
        </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">async</span><span style="color: #000000;"> Task Invoke(HttpContext context)
        {<br></span><span style="color: #0000ff;">            var</span> obj = context.RequestServices.GetRequiredService&lt;ArticleController&gt;<span style="color: #000000;">().GetArticleList();
            </span><span style="color: #0000ff;">var</span> str =<span style="color: #000000;"> JsonConvert.SerializeObject(obj);
            </span><span style="color: #0000ff;">await</span><span style="color: #000000;"> context.Response.WriteAsync(str);
        }
    }</span></pre>
    
    复制代码
    ArticleController的定义如下:
    复制代码
        public class ArticleController : Controller
        {
            private readonly SalesContext _context;
            private readonly ILogger _logger;
            private readonly IMemoryCache _cache;
    
        </span><span style="color: #0000ff;">public</span> ArticleController(SalesContext context, ILogger&lt;ArticleController&gt;<span style="color: #000000;"> logger, IMemoryCache memoryCache)
        {
            _context </span>=<span style="color: #000000;"> context;
            _logger </span>=<span style="color: #000000;"> logger;
            _cache </span>=<span style="color: #000000;"> memoryCache;
        }
    
        [HttpGet]
        </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> ResultObject GetArticleList()
        {
            _logger.LogInformation(</span><span style="color: #800000;">"</span><span style="color: #800000;">==GetArticleList==</span><span style="color: #800000;">"</span><span style="color: #000000;">);
    
            List</span>&lt;Article&gt; articles =<span style="color: #000000;"> _context.Articles
                .AsNoTracking()
                .ToList();
    
            </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> ResultObject
            {               
                result </span>=<span style="color: #000000;"> articles
            };
        }  </span><span style="color: #000000;">
    }</span></pre>
    
    复制代码

    要在中间件中通过依赖使用该Controller,需要将其注册到DI容器:

     public void ConfigureServices(IServiceCollection services)
    { 
          services.AddScoped<ArticleController>();
    }

    以上中间件实现了一个文章信息查询的功能,如果在此中间件内先判断路径,再根据不同的路径调用不同的Contorller提供的服务,就可以形成一个简单的MVC中间件了。

    四、中间件的用途

     中间件的使用体现了AOP(面向切片)的编程思想,在不修改现有代码的情况下,通过增加一些中间件实现一些特定逻辑,可以做的事情很多,比如:

    URL重写

    缓存处理

    异常处理

    用户认证

    五、中间件的注册顺序

    前文提到中间件的注册顺序是比较重要的,建议的顺序如下:

    1. 异常/错误处理
    2. 静态文件服务器
    3. 身份验证
    4. MVC

  • 相关阅读:
    2019.1.1-11 总结
    配置文件的选择
    2019.1.1-5 总结
    shell 脚本
    2017- 感谢自己
    debug和release版本的区别
    linux 下一些命令
    Python中getopt()函数的使用
    开发环境的一些基本认识
    Leetcode 49. Group Anagrams
  • 原文地址:https://www.cnblogs.com/owenzh/p/11206756.html
Copyright © 2011-2022 走看看