3u8583航班,稀饭网,cz6288
前言
由于是第一次写博客,如果您看到此文章,希望大家抱着找错误、批判的心态来看。 sky!
何为中间件?
在 asp.net framework 中应该都知道请求管道。可参考: 系列,个人感觉超详细。
题外话:
说到请求管道,就想以前还是超菜鸟时有次面试被问到这个问题,一脸懵逼只说了 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委托
其他说明
我们看到有很多内置的中间件的用法是*use**,其实是加了个扩展:
public static class requestculturemiddlewareextensions { public static iapplicationbuilder userequestculture( this iapplicationbuilder builder) { return builder.usemiddleware<requestculturemiddleware>(); } }
总结
第一次写博客,最大的感触就是慢,然后就是思维逻辑有点混乱,总想用最简单的语言来表达,就是掌握不好。最后看起来还是太啰嗦了点。最后说明,以上很可能有错误的说法,希望大家以批判的角度来看,有任何问题可在留言区留言!thanks!
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
Blazor server side 自家的一些开源的, 实用型项目的进度之 CEF客户端
.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)
vue+.netcore可支持业务代码扩展的开发框架 VOL.Vue 2.0版本发布
网友评论