在Ocelot中使用自定义的中间件(二)

2023-11-06 07:32

本文主要是介绍在Ocelot中使用自定义的中间件(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在上文中《在Ocelot中使用自定义的中间件(一)》,我介绍了如何在Ocelot中使用自定义的中间件来修改下游服务的response body。今天,我们再扩展一下设计,让我们自己设计的中间件变得更为通用,使其能够应用在不同的Route上。比如,我们可以设计一个通用的替换response body的中间件,然后将其应用在多个Route上。

Ocelot的配置文件

我们可以将Ocelot的配置信息写在appsettings.json中,当然也可以将其放在单独的json文件里,然后通过ConfigureAppConfiguration的调用,将单独的json文件添加到配置系统中。无论如何,基于JSON文件的Ocelot配置都是可以加入我们自定义的内容的,基于数据库的或者其它存储的配置文件信息或许扩展起来并不方便,因此,使用JSON文件作为配置源还是一个不错的选择。比如,我们可以在ReRoute的某个配置中添加以下内容:


{

  "DownstreamPathTemplate": "/api/themes",

  "DownstreamScheme": "http",

  "DownstreamHostAndPorts": [

    {

      "Host": "localhost",

      "Port": 5010

    }

  ],

  "UpstreamPathTemplate": "/themes-api/themes",

  "UpstreamHttpMethod": [ "Get" ],

  "CustomMiddlewares": [

    {

      "Name": "themeCssMinUrlReplacer",

      "Enabled": true,

      "Config": {

        "replacementTemplate": "/themes-api/theme-css/{name}"

      }

    }

  ]

}

然后就需要有一个方法能够解析这部分配置内容。为了方便处理,可以增加以下配置Model,专门存放CustomMiddlewares下的配置信息:


public class CustomMiddlewareConfiguration

{

    public string DownstreamPathTemplate { get; set; }

    public string UpstreamPathTemplate { get; set; }

    public int ReRouteConfigurationIndex { get; set; }

    public string Name { get; set; }

    public bool Enabled { get; set; }

    public Dictionary<string, object> Config { get; set; }

}

然后定义下面的扩展方法,用以从IConfiguration对象中解析出所有的CustomMiddleware的配置信息:


public static IEnumerable<CustomMiddlewareConfiguration> GetCustomMiddlewareConfigurations(this IConfiguration config)

{

    var reRoutesConfigSection = config.GetSection("ReRoutes");

    if (reRoutesConfigSection.Exists())

    {

        var reRoutesConfigList = reRoutesConfigSection.GetChildren();

        for (var idx = 0; idx < reRoutesConfigList.Count(); idx++)

        {

            var reRouteConfigSection = reRoutesConfigList.ElementAt(idx);

            var upstreamPathTemplate = reRouteConfigSection.GetSection("UpstreamPathTemplate").Value;

            var downstreamPathTemplate = reRouteConfigSection.GetSection("DownstreamPathTemplate").Value;

            var customMidwareConfigSection = reRouteConfigSection.GetSection("CustomMiddlewares");

            if (customMidwareConfigSection.Exists())

            {

                var customMidwareConfigList = customMidwareConfigSection.GetChildren();

                foreach (var customMidwareConfig in customMidwareConfigList)

                {

                    var customMiddlewareConfiguration = customMidwareConfig.Get<CustomMiddlewareConfiguration>();

                    customMiddlewareConfiguration.UpstreamPathTemplate = upstreamPathTemplate;

                    customMiddlewareConfiguration.DownstreamPathTemplate = downstreamPathTemplate;

                    customMiddlewareConfiguration.ReRouteConfigurationIndex = idx;

                    yield return customMiddlewareConfiguration;

                }

            }

        }

    }

 

    yield break;

}

CustomMiddleware基类

为了提高程序员的开发体验,我们引入CustomMiddleware基类,在Invoke方法中,CustomMiddleware对象会读取所有的CustomMiddleware配置信息,并找到属于当前ReRoute的CustomMiddleware配置信息,从而决定当前的CustomMiddleware是否应该被执行。相关代码如下:


public abstract class CustomMiddleware : OcelotMiddleware

{

    #region Private Fields

 

    private readonly ICustomMiddlewareConfigurationManager customMiddlewareConfigurationManager;

    private readonly OcelotRequestDelegate next;

 

    #endregion Private Fields

 

    #region Protected Constructors

 

    protected CustomMiddleware(OcelotRequestDelegate next,

        ICustomMiddlewareConfigurationManager customMiddlewareConfigurationManager,

        IOcelotLogger logger) : base(logger)

    {

        this.next = next;

        this.customMiddlewareConfigurationManager = customMiddlewareConfigurationManager;

    }

 

    #endregion Protected Constructors

 

    #region Public Methods

 

    public async Task Invoke(DownstreamContext context)

    {

        var customMiddlewareConfigurations = from cmc in this

                                                .customMiddlewareConfigurationManager

                                                .GetCustomMiddlewareConfigurations()

                                             where cmc.DownstreamPathTemplate == context

                                                    .DownstreamReRoute

                                                    .DownstreamPathTemplate

                                                    .Value &&

                                                   cmc.UpstreamPathTemplate == context

                                                    .DownstreamReRoute

                                                    .UpstreamPathTemplate

                                                    .OriginalValue

                                             select cmc;

 

        var thisMiddlewareName = this.GetType().GetCustomAttribute<CustomMiddlewareAttribute>(false)?.Name;

        var customMiddlewareConfiguration = customMiddlewareConfigurations.FirstOrDefault(x => x.Name == thisMiddlewareName);

        if (customMiddlewareConfiguration?.Enabled ?? false)

        {

            await this.DoInvoke(context, customMiddlewareConfiguration);

        }

 

        await this.next(context);

    }

 

    #endregion Public Methods

 

    #region Protected Methods

 

    protected abstract Task DoInvoke(DownstreamContext context, CustomMiddlewareConfiguration configuration);

 

    #endregion Protected Methods

}

接下来就简单了,只需要让自定义的Ocelot中间件继承于CustomMiddleware基类就行了,当然,为了解耦类型名称与中间件名称,使用一个自定义的CustomMiddlewareAttribute:


[CustomMiddleware("themeCssMinUrlReplacer")]

public class ThemeCssMinUrlReplacer : CustomMiddleware

{

    private readonly Regex regex = new Regex(@"\w+://[a-zA-Z0-9]+(\:\d+)?/themes/(?<theme_name>[a-zA-Z0-9_]+)/bootstrap.min.css");

    public ThemeCssMinUrlReplacer(OcelotRequestDelegate next,

        ICustomMiddlewareConfigurationManager customMiddlewareConfigurationManager,

        IOcelotLoggerFactory loggerFactory)

        : base(next, customMiddlewareConfigurationManager, loggerFactory.CreateLogger<ThemeCssMinUrlReplacer>())

    {

    }

 

    protected override async Task DoInvoke(DownstreamContext context, CustomMiddlewareConfiguration configuration)

    {

        var downstreamResponseString = await context.DownstreamResponse.Content.ReadAsStringAsync();

        var downstreamResponseJson = JObject.Parse(downstreamResponseString);

        var themesArray = (JArray)downstreamResponseJson["themes"];

        foreach(var token in themesArray)

        {

            var cssMinToken = token["cssMin"];

            var cssMinValue = cssMinToken.Value<string>();

            if (regex.IsMatch(cssMinValue))

            {

                var themeName = regex.Match(cssMinValue).Groups["theme_name"].Value;

                var replacementTemplate = configuration.Config["replacementTemplate"].ToString();

                var replacement = $"{context.HttpContext.Request.Scheme}://{context.HttpContext.Request.Host}{replacementTemplate}"

                    .Replace("{name}", themeName);

                cssMinToken.Replace(replacement);

            }

        }

 

        context.DownstreamResponse = new DownstreamResponse(

            new StringContent(downstreamResponseJson.ToString(Formatting.None), Encoding.UTF8, "application/json"),

            context.DownstreamResponse.StatusCode, context.DownstreamResponse.Headers, context.DownstreamResponse.ReasonPhrase);

    }

}

自定义中间件的注册

在上文介绍的BuildCustomOcelotPipeline扩展方法中,加入以下几行,就完成所有自定义中间件的注册:


var customMiddlewareTypes = from type in typeof(Startup).Assembly.GetTypes()

                            where type.BaseType == typeof(CustomMiddleware) &&

                                  type.IsDefined(typeof(CustomMiddlewareAttribute), false)

                            select type;

foreach (var customMiddlewareType in customMiddlewareTypes)

{

    builder.UseMiddleware(customMiddlewareType);

}

当然,app.UseOcelot的调用要调整为:

1

app.UseOcelot((b, c) => b.BuildCustomOcelotPipeline(c).Build()).Wait();

运行

重新运行API网关,得到结果跟之前的一样。所不同的是,我们可以将ThemeCssMinUrlReplacer在其它的ReRoute配置上重用了。

这篇关于在Ocelot中使用自定义的中间件(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/355300

相关文章

MySQL的ALTER TABLE命令的使用解读

《MySQL的ALTERTABLE命令的使用解读》:本文主要介绍MySQL的ALTERTABLE命令的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、查看所建表的编China编程码格式2、修改表的编码格式3、修改列队数据类型4、添加列5、修改列的位置5.1、把列

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

SpringBoot使用ffmpeg实现视频压缩

《SpringBoot使用ffmpeg实现视频压缩》FFmpeg是一个开源的跨平台多媒体处理工具集,用于录制,转换,编辑和流式传输音频和视频,本文将使用ffmpeg实现视频压缩功能,有需要的可以参考... 目录核心功能1.格式转换2.编解码3.音视频处理4.流媒体支持5.滤镜(Filter)安装配置linu

Redis中的Lettuce使用详解

《Redis中的Lettuce使用详解》Lettuce是一个高级的、线程安全的Redis客户端,用于与Redis数据库交互,Lettuce是一个功能强大、使用方便的Redis客户端,适用于各种规模的J... 目录简介特点连接池连接池特点连接池管理连接池优势连接池配置参数监控常用监控工具通过JMX监控通过Pr

apache的commons-pool2原理与使用实践记录

《apache的commons-pool2原理与使用实践记录》ApacheCommonsPool2是一个高效的对象池化框架,通过复用昂贵资源(如数据库连接、线程、网络连接)优化系统性能,这篇文章主... 目录一、核心原理与组件二、使用步骤详解(以数据库连接池为例)三、高级配置与优化四、典型应用场景五、注意事

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

Linux系统之stress-ng测压工具的使用

《Linux系统之stress-ng测压工具的使用》:本文主要介绍Linux系统之stress-ng测压工具的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、理论1.stress工具简介与安装2.语法及参数3.具体安装二、实验1.运行8 cpu, 4 fo

Java使用MethodHandle来替代反射,提高性能问题

《Java使用MethodHandle来替代反射,提高性能问题》:本文主要介绍Java使用MethodHandle来替代反射,提高性能问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录一、认识MethodHandle1、简介2、使用方式3、与反射的区别二、示例1、基本使用2、(重要)

使用C#删除Excel表格中的重复行数据的代码详解

《使用C#删除Excel表格中的重复行数据的代码详解》重复行是指在Excel表格中完全相同的多行数据,删除这些重复行至关重要,因为它们不仅会干扰数据分析,还可能导致错误的决策和结论,所以本文给大家介绍... 目录简介使用工具C# 删除Excel工作表中的重复行语法工作原理实现代码C# 删除指定Excel单元