引言

很多看了上一章的朋友私信博主,问如何自定义,自己的中间件(Middleware),毕竟在实际的项目中,大家会有很多需求要用到中间件,比如防盗链、缓存、日志等等功能,于是博主这边就简单讲解一下框架、组件惯用的优雅手法,官方也推荐这种写法,这样会使得我们扩展性更好,也不会破坏原本结构。

什么是中间件

中间件是一种装配到应用管道以处理请求响应的软件。 每个组件:

  • 选择是否将请求传递到管道中的下一个组件。
  • 可在管道中的下一个组件前后执行工作。

使用 RunMap 和 Use 扩展方法来配置请求委托,请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。

简单的说,我们按需求决定使用哪些组件,程序运行时,一个HTTP请求过来,程序执行流程,是按照我们定义的组件顺序执行的。所以我们项目上的中间件放置顺序是不能乱的,并且不用的也不要装配,避免消耗性能。

概念图:

如果想做其他相关了解,博主建议直接在官网上看文档,微软的文档写的还是很好的,还提供的Demo下载,比很多网上博客讲的好。

微软官网地址:ASP.NET Core 中间件 | Microsoft Docs

接下来进入正题,我们写一个,把所有http请求地址发送到MQ的中间件:

1.编写MsgMiddleware类

这里我们就使用直接编写Middleware类的方式,大家也可以使用实现IMiddleware接口的方式。两种底层原理不一样:前者是框架启动时就实例化;后者是请求来时才实例化,用完立即释放。

编写Middleware类注意:

  1. 编写好InvokeAsync或者Invoke方法
  2. 构造函数参数需要一个RequestDelegate类型的委托

因为这块源码是直接通过反射创建和调用的,不过源码也会对我们的类进行规范校验。

代码逻辑:

这里我们就实现一个简单逻辑,根据Options配置SendFlag是否开启发送MQ,如果是,就调用我们的ISendMessage发送服务。服务获取方式大家也可以使用注入的方式

ISendMessage服务、Options配置类代码,我放到了后面讲解,毕竟逻辑简单,而且也不是重点。

查看代码
  1. public class MsgMiddleware
  2. {
  3. private readonly RequestDelegate _next;
  4. private readonly MsgOptions options;
  5. /// <summary>
  6. /// 管道执行到该中间件时候下一个中间件的RequestDelegate请求委托,如果有其它参数,也同样通过注入的方式获得
  7. /// </summary>
  8. /// <param name="next"></param>
  9. public MsgMiddleware(RequestDelegate next, IOptions<MsgOptions> options)
  10. {
  11. //通过注入方式获得对象
  12. _next = next;
  13. this.options = options.Value;
  14. }
  15. /// <summary>
  16. /// 自定义中间件要执行的逻辑
  17. /// </summary>
  18. /// <param name="context"></param>
  19. /// <returns></returns>
  20. public async Task Invoke(HttpContext context)
  21. {
  22. if (options.SendFlag)
  23. {
  24. //通过IOC获取ISendMessage
  25. ISendMessage _message = context.RequestServices.GetService<ISendMessage>();
  26. _message.Send(context.Request.Path.Value);
  27. }
  28. //把context传进去执行下一个中间件
  29. await _next(context);
  30. }
  31. }

2.编写IApplicationBuilder扩展方法

这里我们就定义两个扩展方法,用来应对在Use时候直接配置Options,或者后面通过IOC方式配置Options

注意:如果在Use时候直接配置参数,我们的Options需要通过Options.Create(op)帮我们包裹成IOptions<>类型,因为编写的中间件参数是Options模式的一个IOptions接口

查看代码
  1. public static class MsgBuilderMiddlewareExtensions
  2. {
  3. //没有Option,依靠IOC的Add的方式设置
  4. public static IApplicationBuilder UseMsgSend(this IApplicationBuilder app)
  5. {
  6. if (app == null)
  7. {
  8. throw new ArgumentNullException(nameof(app));
  9. }
  10. return app.UseMiddleware<MsgMiddleware>();
  11. }
  12. //Use直接配置Options
  13. public static IApplicationBuilder UseMsgSend(this IApplicationBuilder app, Action<MsgOptions> optionsAction)
  14. {
  15. if (app == null)
  16. {
  17. throw new ArgumentNullException(nameof(app));
  18. }
  19. //1 不能直接初始化Option
  20. //2 也不能找到ServiceCollection去初始化了
  21. MsgOptions op = new MsgOptions();
  22. optionsAction(op);
  23. return app.UseMiddleware<MsgMiddleware>(Options.Create(op));
  24. }
  25. }

3.编写IServiceCollection容器扩展方法

这样的好处是,我们这样可以集中注册内部映射,隐藏实现细节,外部不需要关心,这个好处博主就不多说了,因为大家使用别人写的组件也心有体会,对于使用者来说只需要关心怎么用,他内部有多么复杂的逻辑是不知道的,也不需要知道.

这里也是编写两个扩展方法,用来应对在Use时候直接配置Options,或者后面通过IOC方式配置Options

代码逻辑:

这两个方法里面逻辑就是,注册好业务服务、Options 等等。。。

查看代码
  1. /// <summary>
  2. /// 这样可以集中注册内部映射,外部不需要关心
  3. /// </summary>
  4. public static class ServiceCollectionExtensions
  5. {
  6. /// <summary>
  7. /// 配置信息初始化由Middleware
  8. /// </summary>
  9. /// <param name="services"></param>
  10. /// <returns></returns>
  11. public static IServiceCollection AddSendMessage(this IServiceCollection services)
  12. {
  13. return services.AddSingleton<ISendMessage, SendMessage>();
  14. }
  15. /// <summary>
  16. /// 配置信息直接用Option的模式去初始化
  17. /// </summary>
  18. /// <param name="services"></param>
  19. /// <param name="configure"></param>
  20. /// <returns></returns>
  21. public static IServiceCollection AddSendMessage(this IServiceCollection services, Action<MsgOptions> configure)
  22. {
  23. MsgOptions msg = new MsgOptions();
  24. configure(msg);
  25. services.Configure(configure);
  26. services.Configure(msg.RabbitMQOptions);
  27. return services.AddSendMessage();
  28. }
  29. }

4.定义Options类

定义好我们业务逻辑需要的配置信息Options类

查看代码
  1. //MQ配置类
  2. public class RabbitMQOptions
  3. {
  4. public string IP { get; set; }
  5. public string Port { get; set; }
  6. }
  7. //Msg配置类
  8. public class MsgOptions
  9. {
  10. //是否发送
  11. public bool SendFlag { get; set; }
  12. //MQ配置
  13. internal Action<RabbitMQOptions>? RabbitMQOptions { get; private set; }
  14. public void Register(Action<RabbitMQOptions> action)
  15. {
  16. RabbitMQOptions = action;
  17. }
  18. }
  19. //Msg配置扩展方法,用来设置其MQ配置信息
  20. public static class MsgOptionsExtensions
  21. {
  22. public static void UseRabbitMQ(this MsgOptions options, Action<RabbitMQOptions> configure)
  23. {
  24. if (configure == null)
  25. {
  26. throw new ArgumentNullException(nameof(configure));
  27. }
  28. options.Register(configure);
  29. }
  30. }

5.编写Msg服务

这里我们就用打印信息的方式来表示我们将数据发送至MQ了

  1. public interface ISendMessage
  2. {
  3. void Send(string message);
  4. }
  5. public class SendMessage : ISendMessage
  6. {
  7. private readonly RabbitMQOptions rabbitMQ;
  8. public SendMessage(IOptions<RabbitMQOptions> rabbitMQ)
  9. {
  10. this.rabbitMQ = rabbitMQ.Value;
  11. }
  12. public void Send(string message)
  13. {
  14. Console.WriteLine($"发送消息:{message},至--->{rabbitMQ.IP}:{rabbitMQ.Port}服务器");
  15. }
  16. }

6.使用自定义中间件

我们只需要调用我们定义的扩展方法即可,博主这里用的.NET 6,使用的顶级语句。老版本的朋友就在Startup类里配置,调用方式是一样的

如下:

  1. Configure方法里使用,app.UseMsgSend()
  2. ConfigureServices方法里使用,services.AddSendMessage(x=>{...})
查看代码
  1. using WebApplication1.MiddlewareExp;
  2. using WebApplication1.MiddlewareExp.Middleware;
  3. var builder = WebApplication.CreateBuilder(args);
  4. // Add services to the container.
  5. builder.Services.AddControllers();
  6. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
  7. builder.Services.AddEndpointsApiExplorer();
  8. builder.Services.AddSwaggerGen();
  9. //配置中间件配置信息
  10. builder.Services.AddSendMessage(c =>
  11. {
  12. c.SendFlag = true;
  13. c.UseRabbitMQ(x =>
  14. {
  15. x.IP = "127.0.0.1";
  16. x.Port = "8080";
  17. });
  18. });
  19. var app = builder.Build();
  20. // Configure the HTTP request pipeline.
  21. if (app.Environment.IsDevelopment())
  22. {
  23. app.UseSwagger();
  24. app.UseSwaggerUI();
  25. }
  26. // 使用自定义的Msg中间件
  27. app.UseMsgSend();
  28. app.UseAuthorization();
  29. app.MapControllers();
  30. app.Run();

运行效果

如图,我们请求多少次,请求都会经过我们中间件。

.NET Core 自定义中间件 Middleware的更多相关文章

  1. 如何在ASP.NET Core自定义中间件中读取Request.Body和Response.Body的内容?

    原文:如何在ASP.NET Core自定义中间件中读取Request.Body和Response.Body的内容? 文章名称: 如何在ASP.NET Core自定义中间件读取Request.Body和 ...

  2. ASP.NET Core 开发-中间件(Middleware)

    ASP.NET Core开发,开发并使用中间件(Middleware). 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 每个组件选择是否传递给管道中的下一个组件的请求,并能之前和下一组 ...

  3. 如何传递参数给ASP.NET Core的中间件(Middleware)

    问题描述 当我们在ASP.NET Core中定义和使用中间件(Middleware)的时候,有什么好的办法可以给中间件传参数吗? 解决方案 在ASP.NET Core项目中添加一个POCO类来传递参数 ...

  4. ASP.NET Core自定义中间件的方式

    ASP.NET Core应用本质上,其实就是由若干个中间件构建成的请求处理管道.管道相当于一个故事的框架,而中间件就相当于故事中的某些情节.同一个故事框架采用不同的情节拼凑,最终会体现出不同风格的故事 ...

  5. asp.net core 自定义中间件和service

    首先新建项目看下main方法: public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel ...

  6. asp.net core 系列之中间件进阶篇-编写自定义中间件(middleware)

    中间件是被用到管道(pipeline)上来处理请求(request)和响应的(response). asp.net core 本身提供了一些内置的中间件,但是有一些场景,你可能会需要写一些自定义的中间 ...

  7. asp.net core 自定义中间件

    官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1 中间件的定 ...

  8. asp.net core 自定义中间件【以dapper为例】

    在asp.net core开发中.按照国际案例开始.都是先在Nuget安装XXX包.比如我们今天要用到的Dapper nuget里面安装Dapper 1.然后新建一个类文件DapperExtensio ...

  9. django 自定义中间件 middleware

    Django 中间件 Django中的中间件是一个轻量级.底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出.中间件的设计为开发者提供了一种无侵入式的开发方式,增强 ...

随机推荐

  1. 区块链开发学习第七章:第一个Dapp-猜拳游戏

    第一个简单的Dapp-猜拳游戏.本智能合约的功能很简单,就是用户与电脑猜拳,用户选择出手后,电脑随机一个选项,然后调用智能合约方法把两个选项值传过去,在智能合约上进行比较,并通过区块链合约事件广播结果 ...

  2. python 快速启动http监听服务

    python3 [root@vm10-20-9-45 ~]# python3 -m http.server 2378 Serving HTTP on 0.0.0.0 port 2378 (http:/ ...

  3. 如何用uniapp+vue开发自定义相机插件——拍照+录像功能

    调用手机的相机功能并实现拍照和录像是很多APP与插件都必不可少的一个功能,今天智密科技就来分享一下如何基于uniapp + vue实现自定义相机界面,并且实现: 1: 自定义拍照 2: 自定义录像 3 ...

  4. C# 使用TimeSpan秒数转化为时分秒的写法

    1.TimeSpan的生成方法 // 参数: // ticks: // A time period expressed in 100-nanosecond units. public TimeSpan ...

  5. -bash: /etc/ld.so.preload: Operation not permitted处理

    执行 chattr -i /etc/ld.so.preload 执行 chattr -a /etc/ld.so.preload

  6. 【LeetCode】934. Shortest Bridge 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS + BFS 相似题目 参考资料 日期 题目地 ...

  7. 【LeetCode】144. Binary Tree Preorder Traversal 解题报告(Python&C++&Java)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 迭代 日期 题目地址:https://leetc ...

  8. 【LeetCode】728. Self Dividing Numbers 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 循环 filter函数 数字迭代 日期 题目地址:h ...

  9. 【LeetCode】880. Decoded String at Index 解题报告(Python)

    [LeetCode]880. Decoded String at Index 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博 ...

  10. 【LeetCode】390. Elimination Game 解题报告(Python)

    [LeetCode]390. Elimination Game 解题报告(Python) 标签: LeetCode 题目地址:https://leetcode.com/problems/elimina ...