循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)【有源码】
系列目录
本系列涉及到的源码下载地址: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<DemoAMiddleware><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<DemoBMiddleware><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<RequestRecordMiddleware><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;"><DemoAMiddleware>();
app.UseMiddleware<DemoBMiddleware>();
app.UseMiddleware<RequestRecordMiddleware></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<RequestRecordMiddleware><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<DemoAMiddleware><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<DemoAMiddleware><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<ArticleController><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<ArticleController><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><Article> 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
循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)【有源码】的更多相关文章
- 循序渐进学.Net Core Web Api开发系列【0】:序言与目录
一.序言 我大约在2003年时候开始接触到.NET,最初在.NET framework 1.1版本下写过代码,曾经做过WinForm和ASP.NET开发.大约在2010年的时候转型JAVA环境,这么多 ...
- 循序渐进学.Net Core Web Api开发系列【16】:应用安全续-加密与解密
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 应用安全除 ...
- 循序渐进学.Net Core Web Api开发系列【15】:应用安全
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍W ...
- 循序渐进学.Net Core Web Api开发系列【14】:异常处理
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍异 ...
- 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍如 ...
- 循序渐进学.Net Core Web Api开发系列【12】:缓存
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍如 ...
- 循序渐进学.Net Core Web Api开发系列【11】:依赖注入
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇介绍如 ...
- 循序渐进学.Net Core Web Api开发系列【10】:使用日志
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.本篇概述 本篇介 ...
- 循序渐进学.Net Core Web Api开发系列【9】:常用的数据库操作
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇描述一 ...
- 循序渐进学.Net Core Web Api开发系列【8】:访问数据库(基本功能)
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇讨论如 ...
随机推荐
- mysql数值字符串类型的按照数值进行排序
今天遇到一个问题,就是对mysql数值字符串类型进行排序,在默认情况下使用order by 字段名称 desc/asc 进行排序的时候,mysql进行的排序规则是按照ASCII码进行排序的,并不会自动 ...
- Linux下安装git本地库与服务器端远程库
1. git是一个分布式版本管理系统,关于该工具的详细介绍,我认为廖雪峰老师介绍的非常全面:https://www.liaoxuefeng.com/wiki/896043488029600. 不 ...
- JavaScript学习笔记(基础部分)
一.JavaScript简介: 概念:JavaScript是一种解释性的.跨平台的.基于对象的脚本语言,一般用于客户端来给HTML页面增加动态的功能. 组成: 1.ECMAScript,描述了该语言的 ...
- linux+Apache开启伪静态配置
linux+Apache伪静态配置 一.环境准备:CentOS Linux release 7.4.1708 (Core)1.配置源[root@localhost ~]#yum install -y ...
- db2 连接数据库与断开数据库
连接数据库: connect to db_name user db_user using db_pass 断开连接: connect resetdisconnect current quit是退出交 ...
- docker 安装Filebeat
1.查询镜像 docker search filebeat 2.拉取镜像 我此处选择的是prima/filebeat docker pull prima/filebeat 3.创建配置文件 fileb ...
- python socket的长连接和短连接
前言 socket中意为插座,属于进程间通信的一种方式.socket库隐藏了底层,让我们更好的专注于逻辑.如果短连接和长连接两概率没搞明白,会被坑的爬不起来. 短连接 一次完整的传输过程,发送方输出流 ...
- 力扣——Next Permutation(下一个排列) python实现
题目描述: 中文: 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列. 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列). 必须原地修改,只允许 ...
- CNN基础二:使用预训练网络提取图像特征
上一节中,我们采用了一个自定义的网络结构,从头开始训练猫狗大战分类器,最终在使用图像增强的方式下得到了82%的验证准确率.但是,想要将深度学习应用于小型图像数据集,通常不会贸然采用复杂网络并且从头开始 ...
- php导出xls,报错:文件格式和扩展名不匹配。该文件可能已损坏或不安全。除非你相信它的来源,否则不要打开它。
打开文件报错如下 在文件头加上如下代码 ob_end_clean(); header('Content-Type:application/vnd.ms-excel'); header('Cache-C ...