在Ocelot中使用自定义的中间件(二)
在上文中《在Ocelot中使用自定義的中間件(一)》,我介紹了如何在Ocelot中使用自定義的中間件來修改下游服務(wù)的response body。今天,我們再擴(kuò)展一下設(shè)計(jì),讓我們自己設(shè)計(jì)的中間件變得更為通用,使其能夠應(yīng)用在不同的Route上。比如,我們可以設(shè)計(jì)一個通用的替換response body的中間件,然后將其應(yīng)用在多個Route上。
Ocelot的配置文件
我們可以將Ocelot的配置信息寫在appsettings.json中,當(dāng)然也可以將其放在單獨(dú)的json文件里,然后通過ConfigureAppConfiguration的調(diào)用,將單獨(dú)的json文件添加到配置系統(tǒng)中。無論如何,基于JSON文件的Ocelot配置都是可以加入我們自定義的內(nèi)容的,基于數(shù)據(jù)庫的或者其它存儲的配置文件信息或許擴(kuò)展起來并不方便,因此,使用JSON文件作為配置源還是一個不錯的選擇。比如,我們可以在ReRoute的某個配置中添加以下內(nèi)容:
{ ??"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}" ??????} ????} ??] } |
然后就需要有一個方法能夠解析這部分配置內(nèi)容。為了方便處理,可以增加以下配置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; } } |
然后定義下面的擴(kuò)展方法,用以從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基類
為了提高程序員的開發(fā)體驗(yàn),我們引入CustomMiddleware基類,在Invoke方法中,CustomMiddleware對象會讀取所有的CustomMiddleware配置信息,并找到屬于當(dāng)前ReRoute的CustomMiddleware配置信息,從而決定當(dāng)前的CustomMiddleware是否應(yīng)該被執(zhí)行。相關(guān)代碼如下:
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基類就行了,當(dāng)然,為了解耦類型名稱與中間件名稱,使用一個自定義的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擴(kuò)展方法中,加入以下幾行,就完成所有自定義中間件的注冊:
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); } |
當(dāng)然,app.UseOcelot的調(diào)用要調(diào)整為:
1 | app.UseOcelot((b, c) => b.BuildCustomOcelotPipeline(c).Build()).Wait(); |
運(yùn)行
重新運(yùn)行API網(wǎng)關(guān),得到結(jié)果跟之前的一樣。所不同的是,我們可以將ThemeCssMinUrlReplacer在其它的ReRoute配置上重用了。
總結(jié)
以上是生活随笔為你收集整理的在Ocelot中使用自定义的中间件(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core开发实战(第24课:文
- 下一篇: 终结“永恒之蓝”后,再战“永恒之黑”