循序渐进学.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

原文地址:https://www.cnblogs.com/seabluescn/p/9345148.html

时间: 07-21

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

循序渐进学.Net Core Web Api开发系列【8】:访问数据库(基本功能)

系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇讨论如何连接数据库,包括连接SQL Server 和 连接MySQL,然后做一些基本的数据操作. 二.连接SQL Server 首先通过NuGet添加相关的包: 新建一个实体类: public class Product { [Key] public string Code { get; set; } p

循序渐进学.Net Core Web Api开发系列【9】:常用的数据库操作

系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇描述一些常用的数据库操作,包括:条件查询.排序.分页.事务等基本数据库操作.试验的数据库为MySQL. 二.条件查询1.查询所有记录 List<Article> articles = _context.Articles.ToList<Article>(); 2.根据主键进行查询 Articl

循序渐进学.Net Core Web Api开发系列【1】:开发环境

系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.本篇概述 本篇不打算描述如何通过Visual Studio创建一个项目之类的话题,主要描述以下内容: 1.使用NuGet和Bower引入第三方库 2.Linux下安装运行环境 3.关于安装虚拟机时碰到的网络设置的问题 实验环境:Windows 10 ,Visual Studio 2017 ,VM 14 , Cent

ASP.NET Core Web API 开发-RESTful API实现

REST 介绍: 符合REST设计风格的Web API称为RESTful API. 具象状态传输(英文:Representational State Transfer,简称REST)是Roy Thomas Fielding博士于2000年在他的博士论文 "Architectural Styles and the Design of Network-based Software Architectures" 中提出来的一种万维网软件架构风格. 目前在三种主流的Web服务实现方案中,因为R

在Mac下创建ASP.NET Core Web API

在Mac下创建ASP.NET Core Web API 在Mac下创建ASP.NET Core Web API 这系列文章是参考了.NET Core文档和源码,可能有人要问,直接看官方的英文文档不就可以了吗,为什么还要写这些文章呢? 原因如下: 官方文档涉及的内容相当全面,属于那种大而全的知识仓库,不太适合初学者,很容易让人失去重要,让人掉入到具体的细节之中. 对于大多数人来讲开发语言只是工具,程序员都有一个通病,就是死磕工具,把工具学深.我认为在工具上没有必要投入太多时间,以能高效地完成日常的

ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现

很长一段时间以来,我都在思考如何在ASP.NET Core的框架下,实现一套完整的事件驱动型架构.这个问题看上去有点大,其实主要目标是为了实现一个基于ASP.NET Core的微服务,它能够非常简单地订阅来自于某个渠道的事件消息,并对接收到的消息进行处理,于此同时,它还能够向该渠道发送事件消息,以便订阅该事件消息的消费者能够对消息数据做进一步处理.让我们回顾一下微服务之间通信的几种方式,分为同步和异步两种.同步通信最常见的就是RESTful API,而且非常简单轻量,一个Request/Resp

ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理

在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发.订阅和处理的流程.这种实现太简单了,百十行代码就展示了一个基本工作原理.然而,要将这样的解决方案运用到实际生产环境,还有很长的路要走.今天,我们就研究一下在事件处理器中,对象生命周期的管理问题. 事实上,不仅仅是在事件处理器中,我们需要关心对象的生命周期,在整个ASP.NET Core Web API的应用程序里,我们需要理解并仔细推敲被注册到IoC容器中的服务,它们的生命周期应该是个怎样的情形,这也是服务端应用程序设

ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

 一.前言 这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自己的职业发展还是很有帮助的.毕竟,现在都快到9102年了,如果你还是只会 Web Form,或许还是能找到很多的工作机会,可是,这真的不再适应未来的发展了.如果你准备继续在 .NET 平台下进行开发,适时开始拥抱开源,拥抱 ASP.NET Core,即使,现在工作中可能用不到. 雪崩发生时,没有一

asp.net core web api token验证和RestSharp访问

对与asp.net core web api验证,多种方式,本例子的方式采用的是李争的<微软开源跨平台移动开发实践>中的token验证方式. Asp.net core web api项目代码: 首先定义三个Token相关的类,一个Token实体类,一个TokenProvider类,一个TokenProviderOptions类 代码如下: /// <summary> /// Token实体 /// </summary> public class TokenEntity