最近问了几个面试同一个问题:如果有多个自定义Middleware,如何控制它们的执行顺序(比如先判断用户合法再写访问日志)。居然大部分人答不上来?!

对此,你有什么看法?

ASP.NET Core Middleware的调用顺序

这里我直接贴上官方文档[1]中的图片:

ASP.NET Core 请求管道包含一系列请求委托,依次调用。而调用顺序实际上就是我们在Startup.cs中注册(使用UseMiddlewareExtensions.UseMiddleware方法)它们的顺序。

示例代码如下:

public void ConfigureServices(IServiceCollection services)
{//注册生命周期services.AddTransient<TransientMiddleware>();services.AddScoped<ScopedMiddleware>();
}public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{//注册调用顺序app.UseMiddleware<TransientMiddleware>();app.UseMiddleware<ScopedMiddleware>();
}

但这种方式,对于调整自定义Middleware的需求,需要经常修改Startup.cs,而且会使代码比较凌乱,可读性较差。

可以使用下面的方式,简化注册代码:

public void ConfigureServices(IServiceCollection services)
{services.AddMiddlewares();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseMiddlewares();
}

实现方式

1.定义Attribute

MiddlewareRegisterAttribute将放在每个Middleware实现类上,表明它是需要被注册的Middleware。

[AttributeUsage(AttributeTargets.Class)]
public class MiddlewareRegisterAttribute : Attribute
{//注册顺序public int Sort { get; set; } = int.MaxValue;//生命周期public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Scoped;
}

2.定义注册信息类

MiddlewareRegisterInfo用于存放Middleware的注册信息,供注册方法调用。

public class MiddlewareRegisterInfo
{public MiddlewareRegisterInfo(Type type,MiddlewareRegisterAttribute attribute){Type = type;Sort = attribute.Sort;Lifetime = attribute.Lifetime;}public Type Type { get; private set; }public int Sort { get; private set; }public ServiceLifetime Lifetime { get; private set; }
}

2.实现注册扩展方法

读取Assembly中的Type, 如果存在MiddlewareRegisterAttribute就把它放入List<MiddlewareRegisterInfo>列表中,最后根据Sort属性顺序依次注册,代码如下:

public static class MiddlewareRegisterExtensions
{private static readonly IEnumerable<MiddlewareRegisterInfo> _middlewareRegisterInfos = GetMiddlewareRegisterInfos();public static IServiceCollection AddMiddlewares(this IServiceCollection services){foreach (var middlewareRegisterInfo in _middlewareRegisterInfos){switch (middlewareRegisterInfo.Lifetime){case ServiceLifetime.Singleton:services.AddSingleton(middlewareRegisterInfo.Type);break;case ServiceLifetime.Transient:services.AddTransient(middlewareRegisterInfo.Type);break;default:services.AddScoped(middlewareRegisterInfo.Type);break;}}return services;}public static IApplicationBuilder UseMiddlewares(this IApplicationBuilder applicationBuilder){foreach (var middlewareRegisterInfo in _middlewareRegisterInfos){applicationBuilder.UseMiddleware(middlewareRegisterInfo.Type);}return applicationBuilder;}private static List<MiddlewareRegisterInfo> GetMiddlewareRegisterInfos(){var middlewareRegisterInfos = new List<MiddlewareRegisterInfo>();//所有包含Middleware的Assemblyvar assemblies = new Assembly[] { typeof(Startup).Assembly };foreach (var assembly in assemblies){foreach (var type in assembly.GetTypes().Where(x => !x.IsAbstract)){var attribute = type.GetCustomAttribute<MiddlewareRegisterAttribute>();if (attribute != null){middlewareRegisterInfos.Add(new MiddlewareRegisterInfo(type, attribute));}}}return middlewareRegisterInfos.OrderBy(p=>p.Sort).ToList();}
}

测试一下

创建3个Middleware,功能仅仅是输出日志:

[MiddlewareRegister(Sort = 100)]
public class OneMiddleware : IMiddleware
{private readonly ILogger<OneMiddleware> logger;public OneMiddleware(ILogger<OneMiddleware> logger){this.logger = logger;}public async Task InvokeAsync(HttpContext context, RequestDelegate next){logger.LogInformation("One");await next(context);}
}

Sort用100、200、300,方便后面修改排序。

运行效果如下图:

现在,将ThreeMiddleware的Sort改为150,调整注册顺序,再次运行,效果如下图:

结论

当然,这个解决方案也存在一些缺点,比如修改排序的位置移到每个Middleware,比较分散。但整体来说,代码更易读

如果你有其他实现方式,欢迎到公众号后台留言指教。

如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!

参考资料

[1]

官方文档: https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware

这批.Net程序员水平不行啊!居然ASP.NET Core Middleware都不会用相关推荐

  1. 程序员水平分级 你属于哪一类?

    程序员水平分级 你属于哪一类? 2012-02-14 16:57 | 14782次阅读 | 来源:whattofix.com [已有69条评论]发表评论 关键词:程序员,编程,评级 | 作者:Dani ...

  2. 程序员水平自测题:程序员们,想知道你的技术达到了什么水平吗?

    程序员甲抱怨:"干IT太苦了,数据是越存越多,预算是越来越少,好基友是越来越多,女朋友是越来越少.想换一行怎么办??" 程序员乙:"敲一下回车." 中国的程序员 ...

  3. 【Rust 日报】2021-07-04 如何在面试中确定 Rust 程序员水平?

    如何在面试中确定 Rust 程序员水平? 对于这个问题,张汉东老师在 RustFriday 飞书群线上沙龙第十三期中进行了分享: 视频:RustFriday 飞书群线上沙龙 第十三期 | [讨论]如何 ...

  4. 34岁程序员面试被拒:混得好的年轻人都有这3个特质

    34岁程序员面试被拒:混得好的年轻人都有这3个特质 前几天我参加了一场闭门创业营,很多大佬分享了很多干货和经验,但是最打动我的,还是其中一位前辈分享的故事--"如何判断一个员工是否有潜力&q ...

  5. Android架构师吐槽腾讯王者荣耀的程序员,排位匹配算法怎么搞的,每次都输

    腾讯王者荣耀的开发来来来出来聊聊,真是日了狗了,多次离上王者还差两三颗星的时候队友就开始水的一塌糊涂,对面就牛逼的不行. 又连跪回去了,被对面把屎都打出来了,实在忍不住来吐槽,你们这个排位匹配算法到底 ...

  6. 程序员的幸福生活——有你的日子,每天都是情人节

    主人公:缘生梦 老婆:华华誓界 2月14日已经过去两天了,这个日子对于很多人来说意义非凡.本来这篇文章打算是这天写的,可是本人有严重的拖延症,被各种事情一耽误就给拖到了第二天,想就这样算了吧,七夕的时 ...

  7. 兄dei,作为程序员的你,这些一直接触的词都念对了吗?

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 很多时候,大家可能都觉得程序员英文一定很好.因为我们每天都对着满屏幕的英文,噼里啪啦一整天.讨论个需求 ...

  8. c语言输出我爱你程序,程序员表白教程,这些代码用过的都成功了!

    原标题:程序员表白教程,这些代码用过的都成功了! 作为一名程序员,如何用自己的技术向喜欢的人表白? 这篇程序员表白教程,可以让你创造出不一样的浪漫! 你值得拥有! 1. I Love You Batc ...

  9. 设计师和程序员必备:全世界最著名的 icon 网站都在这了

    设计师和程序员必备:全世界最著名的 icon 网站都在这了 作为一个多年的独立开发者,收藏了非常多免费无版权图片网站.免费 icons.知名的技术人博客.有趣的网站等等,今天给大家分享我这几年收藏的 ...

最新文章

  1. 【怎样写代码】函数式编程 -- Lambda表达式(三):LINQ初步
  2. spark读取hdfs路径下的数据_Spark读取HDFS数据分区参考
  3. Solr分页与高亮(使用SolrNet实现)
  4. 2020ICPC(小米邀请赛2) - Data Structure Problem(线段树+树状数组)
  5. 函数平移口诀_初三二次函数平移规律的口诀
  6. linux动态追踪神器——Strace实例介绍【转】
  7. 过来人谈在美国大学里的中国研究生
  8. 计算机旅游网站毕业论文,旅游网站的设计与实现(毕业论文)
  9. 设计一个具有大纯时延时间的一阶惯性环节的计算机控制系统,一阶惯性环节的计算机控制课程设计【参考】.doc...
  10. Quartz定时任务框架(一)
  11. 企业需要关注的零信任 24 问
  12. android gsm功能,Android手机-GSM网络与WCDMA网络选择的设置
  13. 桌面点击鼠标右键一直显示转圈卡住如何解决
  14. JAVA Swing主题 简洁扁平化苹果风格主题
  15. 一座古老与现代和谐共融的城市
  16. JAVA基础常见的知识点
  17. arcgis地图加载离线地图
  18. CRM销售管理系统能够给企业带来哪些好处?
  19. 今天,我们这么和霍金说再见
  20. 题解 - CF662C Binary Table

热门文章

  1. saltapi java_搭建基于Jenkins salt-api的运维工具
  2. python函数 global_**Python的函数参数传递 和 global
  3. spring boot拦截器中获取request post请求中的参数(转)
  4. (一)使用appium之前为什么要安装nodejs???
  5. C#构造函数、操作符重载以及自定义类型转换
  6. 初识Spark2.0之Spark SQL
  7. hdu-5781 ATM Mechine(dp+概率期望)
  8. http://www.appinn.com/bookmark-manager-chrome/
  9. Office SharePoint Server 2007
  10. ADO.NET Entity Framework学习笔记(2)建模[转]