ASP.NET Core 中的中间件
前言
由于是第一次写博客,如果您看到此文章,希望大家抱着找错误、批判的心态来看。 sky!
何为中间件?
在 ASP.NET Framework 中应该都知道请求管道。可参考:浅谈 ASP.NET 的内部机制 系列,个人感觉超详细。
题外话:
说到请求管道,就想以前还是超菜鸟时有次面试被问到这个问题,一脸懵逼只说了 Controller→Action→View。脸红啊!!
ASP.NET Core 中的中间件就是.net framework 请求管道的实现。下图演示了 Middlerware 的概念。 沿黑色箭头执行。

每一个中间件(Middleware1、Middleware2...)都是一个委托,这一系列委托就组成了整个管道。
中间件的写法
- 直接在Startup.cs类的Configure方法里写 - app.Use(async (context, next) =>
 {
 logger.LogInformation("中间件开始...");
 await next.Invoke(); //执行下一个中间件
 logger.LogInformation("中间件完成...");
 });
 - 结合上图: - //logic对应logger.LogInformation("中间件开始..."); - next();对应await next.Invoke(); - //more logic对应logger.LogInformation("中间件完成..."); - 其中//logic(即请求)是顺序执行。即:Middleware1→Middleware2→...→Middlewaren - 而//more logic(即响应)是倒序执行。即:Middlewaren→...→Middleware2→Middleware1 
- 同 1,只是不用 Use 而是用 Run: - app.Run(async context =>
 {
 await context.Response.WriteAsync("请求终止了,下一步将会执行已执行过的Middleware的 //more logic");
 });
 - Run 会终止请求,即管道中最后一个中间件,后面详细剖析! 
- 下面这种写法应该是比较合理的,也是比较优雅的 - 新建一个类如下(该类是有强制规范的,详细见下文): - public class RequestTestMiddleware
 {
 private readonly RequestDelegate _next;
 public RequestTestMiddleware(RequestDelegate next)
 {
 _next = next;
 }
 public async Task InvokeAsync(HttpContext context)
 {
 //中间件开始 logic
 await _next(context);//执行下一个中间件
 //中间件完成 more logic
 }
 }
 - 在Startup.cs类的Configure方法里添加如下代码,效果和 1 相同: - app.UseMiddleware<RequestTestMiddleware>();
 //app.UseMiddleware<RequestTestMiddleware>(params object[] parameters);//参数说明见下面
 - 不知发现了没,上面的InvokeAsync方法不是用的打印日志,而是用的注释。 
 因为我们没有引用logger对象,了解过 ASP.NET Core 的肯定知道依赖注入,我们只需要把ILogger注入进来就行了,改造如下:- public class RequestTestMiddleware
 {
 private readonly RequestDelegate _next;
 public RequestTestMiddleware(RequestDelegate next)
 {
 _next = next;
 }
 public async Task InvokeAsync(HttpContext context, ILogger<TestMiddleware> logger)
 {
 logger.LogInformation("中间件开始 logic");
 await _next(context);
 logger.LogInformation("中间件完成 more logic");
 }
 }
 
- 通过依赖注入方法添加中间件: 
 新建类 TestMiddleware.cs 注意依赖注入的位置和 3 不同- public class TestMiddleware : IMiddleware
 {
 private readonly ILogger _logger;
 public TestMiddleware(ILogger<TestMiddleware> logger)
 {
 _logger = logger;
 }
 public async Task InvokeAsync(HttpContext context, RequestDelegate next)
 {
 _logger.LogInformation("中间件开始");
 await next(context);
 _logger.LogInformation("中间件完成");
 }
 }
 - 在Startup.cs类的ConfigureServices方法里添加如下代码: - services.AddTransient<TestMiddleware>();
 - 在Startup.cs类的Configure方法里添加如下代码: - app.UseMiddleware<TestMiddleware>();
 
- 还有一种第三方容器激活中间件 
源代码分析(部分)
- Run和Use的实现 - 直接放出源代码: - public static void Run(this IApplicationBuilder app, RequestDelegate handler)
 {
 if (app == null)
 {
 throw new ArgumentNullException(nameof(app));
 }
 if (handler == null)
 {
 throw new ArgumentNullException(nameof(handler));
 }
 app.Use(_ => handler);
 }
 - public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware)
 {
 return app.Use(next =>
 {
 return context =>
 {
 Func<Task> simpleNext = () => next(context);
 return middleware(context, simpleNext);
 };
 });
 }
 - 2 个方法最终调用的都是app.Use(),我们看下代码: - public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
 {
 _components.Add(middleware);
 return this;
 }
 - _components是IList<Func<RequestDelegate, RequestDelegate>>类型,其实就是把我们的Middleware添加到 _components 中,继续看代码: - public RequestDelegate Build()
 {
 RequestDelegate app = context =>
 {
 context.Response.StatusCode = 404;
 return Task.CompletedTask;
 };
 foreach (var component in _components.Reverse())
 {
 app = component(app);
 }
 return app;
 }
 - 该方法会在Program.cs中Main方法的 CreateWebHostBuilder(args).Build().Run(); 的 Run() 方法执行。 - 此方法把我们所有的Middleware再次组装成 1 个新的RequestDelegate,最终的顺序将会是: - Middleware1()
 {
 next()=>Middleware2()
 {
 next()=>Middleware3()
 {
 next()=>最后的那个返回404的委托
 }
 }
 }
 - 不知道写清楚了没( ╯□╰ ). 其中next()=>Middleware2()的意思为:next()就是 Middleware2() 
- 继承 IMiddleware 和没继承 IMiddleware(根据规范必须要有 InvokeAsync 或 Invoke 方法等)的区别: - 按功能实现方面来说是没区别的,但按性能方面应该是继承了 IMiddleware 的方式要好很多,因为没继承 IMiddleware 的方式会用到反射。(未测试,由于继承 IMiddleware 还需要用依赖注入这里只是猜测) - 代码见: 
 Microsoft.AspNetCore.Http.Abstractions\Extensions\UseMiddlewareExtensions.cs 的 UseMiddleware 方法。
- 未继承 IMiddleware 时的约定,直接看代码吧: - //1.在middleware中必须存在public且有返回值的方法
 var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); //2.必须有‘Invoke’或‘InvokeAsync’方法
 var invokeMethods = methods.Where(m =>
 string.Equals(m.Name, "Invoke", StringComparison.Ordinal)
 || string.Equals(m.Name, "InvokeAsync", StringComparison.Ordinal)
 ).ToArray(); //3.‘Invoke’和‘InvokeAsync’只能有1个
 if (invokeMethods.Length > 1) {} //4.‘Invoke’和‘InvokeAsync’必须要存在
 if (invokeMethods.Length == 0) {}
 var methodInfo = invokeMethods[0]; //5.返回结果类型必须为Task
 if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType)){}
 
- 中间件传参 
 直接上代码:- public class RequestTestMiddleware
 {
 private readonly RequestDelegate _next;
 private int _i;
 public RequestTestMiddleware(RequestDelegate next, int i)
 {
 _next = next;
 _i = i;
 }
 public async Task InvokeAsync(HttpContext context, ILogger<TestMiddleware> logger)
 {
 logger.LogInformation($"通过参数传递i值:{_i}");
 logger.LogInformation("中间件开始");
 await _next(context);
 logger.LogInformation("中间件完成");
 }
 }
 - 在Startup.cs类的Configure方法里: - //参数类型为: params object[] args
 app.UseMiddleware<RequestTestMiddleware>(1);
 - 具体实现方式同样在 Microsoft.AspNetCore.Http.Abstractions\Extensions\UseMiddlewareExtensions.cs 的 UseMiddleware 方法中 
高级用法 Map MapWhen
- Map - app.Map("/map", _app =>
 {
 _app.Run(async context =>
 {
 await context.Response.WriteAsync("Test Map!");
 });
 });
 - 当访问https://localhost:5001/map时将返回 Test Map! - 这里说一下,代码中并没有 MapController.... 
- MapWhen - app.MapWhen(context => context.Request.Query.ContainsKey("branch"), _app =>
 {
 _app.Run(async context =>
 {
 await context.Response.WriteAsync("Test Map!");
 });
 });
 - 看源代码会发现,MapWhen 的第二个参数(委托)并不是上面Use()中next(),而是存在MapOptions的Branch属性中,也是RequestDelegate委托 
其他说明
- Middleware 的执行的有顺序的,在合适的 Middleware 返回请求可时管道更短,速度更快。 
 比如 UseStaticFiles(),静态资源不必走验证、MVC 中间件,所以该方法在中间件的前面执行。
- 我们看到有很多内置的中间件的用法是*Use**,其实是加了个扩展: - public static class RequestCultureMiddlewareExtensions
 {
 public static IApplicationBuilder UseRequestCulture(
 this IApplicationBuilder builder)
 {
 return builder.UseMiddleware<RequestCultureMiddleware>();
 }
 }
 
总结
第一次写博客,最大的感触就是慢,然后就是思维逻辑有点混乱,总想用最简单的语言来表达,就是掌握不好。最后看起来还是太啰嗦了点。最后说明,以上很可能有错误的说法,希望大家以批判的角度来看,有任何问题可在留言区留言!Thanks!
ASP.NET Core 中的中间件的更多相关文章
- 在Asp.net Core中使用中间件来管理websocket
		介绍 ASP.NET Core SignalR是一个有用的库,可以简化Web应用程序中实时通信的管理.但是,我宁愿使用WebSockets,因为我想要更灵活,并且与任何WebSocket客户端兼容. ... 
- 在Asp.Net Core中使用中间件保护非公开文件
		在企业开发中,我们经常会遇到由用户上传文件的场景,比如某OA系统中,由用户填写某表单并上传身份证,由身份管理员审查,超级管理员可以查看. 就这样一个场景,用户上传的文件只能有三种人看得见(能够访问) ... 
- Asp.Net Core 通过自定义中间件防止图片盗链的实例(转)
		一.原理 要实现防盗链,我们就必须先理解盗链的实现原理,提到防盗链的实现原理就不得不从HTTP协议说起,在HTTP协议中,有一个表头字段叫referer,采用URL的格式来表示从哪儿链接到当前的网页或 ... 
- ASP.NET Core系列:中间件
		1. 概述 ASP.NET Core中的中间件是嵌入到应用管道中用于处理请求和响应的一段代码. 2. 使用 IApplicationBuilder 创建中间件管道 2.1 匿名函数 使用Run, Ma ... 
- ASP.NET Core 中的那些认证中间件及一些重要知识点
		前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ... 
- [转]ASP.NET Core 中的那些认证中间件及一些重要知识点
		本文转自:http://www.qingruanit.net/c_all/article_6645.html 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系 ... 
- 在ASP.NET Core 中使用Cookie中间件
		在ASP.NET Core 中使用Cookie中间件 ASP.NET Core 提供了Cookie中间件来序列化用户主题到一个加密的Cookie中并且在后来的请求中校验这个Cookie,再现用户并且分 ... 
- ASP.NET Core中使用GraphQL - 第二章 中间件
		前文:ASP.NET Core中使用GraphQL - 第一章 Hello World 中间件 如果你熟悉ASP.NET Core的中间件,你可能会注意到之前的博客中我们已经使用了一个中间件, app ... 
- ASP.NET Core 中的SEO优化(2):中间件中渲染Razor视图
		前言 上一篇文章<ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存>中介绍了中间件的使用方法.以及使用中间件实现服务端静态化缓存的功能.本系列文章的这些技巧都是我 ... 
随机推荐
- spark远程调试
			基本流程1.远程运行spark,打开Spark master机器的JVM的jdwp,让其阻塞监听指定端口(8888),让其有终端向指定端口发送特定请求再执行:2.IntelliJ配置socket远程连 ... 
- CentOS6.5 下MySQL傻瓜式安装
			为了为服务器上装mysql我先在虚拟机上练习了一下特此记录并分享; 注:参考文章https://www.cnblogs.com/xiaoluo501395377/archive/2013/04/07/ ... 
- C# 保护进程不被结束(源代码)防任务管理器结束进程
			C# 保护进程不被结束(源代码)防任务管理器结束进程 Posted on 2013-03-25 16:03 快乐家++ 阅读(3173) 评论(3) 编辑 收藏 闲来无事,英语又学的太痛苦.看到我妈妈 ... 
- 检测一个DLL文件是x64还是x86
			对于一个DLL,我们如何判定其是32位的还是64位的,或者是any cpu的platform? Visual Studio提供了一个很好的工具:corflags,这个是内嵌到Developer Com ... 
- MySQL    查询数据表里面时间字段为今天添加的计数
			一: 下面这条语句查出来的count值 . 查询类型ID(category_id)为18的,今天插入的数据数, created_on: 为数据表中一字段 datetime类型, 记录此条数据添加的时 ... 
- 蓝桥杯 算法训练 ALGO-140 P1101
			算法训练 P1101 时间限制:1.0s 内存限制:256.0MB 有一份提货单,其数据项目有:商品名(MC).单价(DJ).数量(SL).定义一个结构体prut,其成员是上面的三项数据.在主函 ... 
- 机器学习:逻辑回归(scikit-learn 中的逻辑回归)
			一.基础理解 使用逻辑回归算法训练模型时,为模型引入多项式项,使模型生成不规则的决策边界,对非线性的数据进行分类: 问题:引入多项式项后,模型变的复杂,可能产生过拟合现象: 方案:对模型正则化处理,损 ... 
- 阿里巴巴开源项目: canal 基于mysql数据库binlog的增量订阅&消费
			背景 早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求.不过早期的数据库同步业务,主要是基于trigger的方式获取增 量变更,不过从2010年开始,阿里系公司开始逐步的 ... 
- Python Django框架 补充
			Django REST framework ORM框架整理 Django框架 app间互借models字段的操作 ORM数据库操作补充:models中的一对一操作.过滤.事务 Django model ... 
- 2015.1.15 利用Oracle函数返回表结果 重大技术进步!
			-- sql 调用 select * from table( get_airway_subpoint(x,x,x)) ///////////////////////////////////////// ... 
