• 循序渐进学.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;
    
            public DemoAMiddleware(RequestDelegate next, ILogger<DemoAMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            public async Task Invoke(HttpContext context)
            {
                _logger.LogInformation("(1) DemoAMiddleware.Invoke front");  
                await _next(context);
                _logger.LogInformation("[1] DemoAMiddleware:Invoke back");
            }       
        }
    
      public class DemoBMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger _logger;
    
            public DemoBMiddleware(RequestDelegate next, ILogger<DemoBMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            public async Task Invoke(HttpContext context)
            {
    
                _logger.LogInformation("(2) DemoBMiddleware.Invoke part1");  
                await _next(context);
                _logger.LogInformation("[2] DemoBMiddleware:Invoke part2");
            }      
        }
    
        public class RequestRecordMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger _logger;
    
            public RequestRecordMiddleware(RequestDelegate next, ILogger<RequestRecordMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            public async Task Invoke(HttpContext context)
            {
                _logger.LogInformation("(3) RequestRecordMiddleware.Invoke");
    
                String URL = context.Request.Path.ToString();
                _logger.LogInformation($"URL : {URL}");            
    
                await _next(context);
    
                _logger.LogInformation("[3] RequestRecordMiddleware:Invoke next");
                _logger.LogInformation($"StatusCode : {context.Response.StatusCode}");
            }       
        }

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

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

       public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {            
                services.AddMvc();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
            {
                app.UseUnifyException();
    
                app.UseMiddleware<DemoAMiddleware>();
                app.UseMiddleware<DemoBMiddleware>();           
                app.UseMiddleware<RequestRecordMiddleware>();            
    
                app.UseStaticFiles();
                app.UseMvcWithDefaultRoute();
            }

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

    public static class RequestRecordMiddlewareExtensions
        {
            public static IApplicationBuilder UseRequestRecord(this IApplicationBuilder builder)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException("builder is null");
                }
    
                return builder.UseMiddleware<RequestRecordMiddleware>();
            }
        }

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

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

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

    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;
    
            public MyMvcMiddleware(RequestDelegate next, ILogger<DemoAMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            public async Task Invoke(HttpContext context)
            {  var str = "hello,world!";
                await  context.Response.WriteAsync(str);          
            }       
        }

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

       public class MyMvcMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger _logger;
    
            public MyMvcMiddleware(RequestDelegate next, ILogger<DemoAMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            public async Task Invoke(HttpContext context)
            {
    var obj = context.RequestServices.GetRequiredService<ArticleController>().GetArticleList(); var str = JsonConvert.SerializeObject(obj); await context.Response.WriteAsync(str); } }
    ArticleController的定义如下:
        public class ArticleController : Controller
        {
            private readonly SalesContext _context;
            private readonly ILogger _logger;
            private readonly IMemoryCache _cache;
    
            public ArticleController(SalesContext context, ILogger<ArticleController> logger, IMemoryCache memoryCache)
            {
                _context = context;
                _logger = logger;
                _cache = memoryCache;
            }
    
            [HttpGet]
            public ResultObject GetArticleList()
            {
                _logger.LogInformation("==GetArticleList==");
    
                List<Article> articles = _context.Articles
                    .AsNoTracking()
                    .ToList();
    
                return new ResultObject
                {               
                    result = articles
                };
            }  
        }

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

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

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

    四、中间件的用途

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

    URL重写

    缓存处理

    异常处理

    用户认证

    五、中间件的注册顺序

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

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

  • 相关阅读:
    leetcode Power of Two
    leetcode Merge Two Sorted Lists
    Mac linux 安装memcached服务 用法
    Vsftp配置都没有问题 连接不上 530 Login incorrect 解决方法
    mysql 远程连接不上 %用户已经添加了
    Centos yum 安装mysql报错 No package mysql-server available.
    Linux 查询程序安装路径 是否安装
    php报错 Call to undefined function mb_stripos()
    mac编译openssl扩展报错 openssl.c:44:10: fatal error: 'openssl/evp.h' file not found
    在安装mysqli的时候,出现error: ext/mysqlnd/mysql_float_to_double.h: No such file or direc
  • 原文地址:https://www.cnblogs.com/seabluescn/p/9345148.html
Copyright © 2020-2023  润新知