讓 Ocelot 與 asp.net core “共存”

来源:https://www.cnblogs.com/weihanli/archive/2019/05/22/integrate-ocelot-route-with-aspnetcore.html
-Advertisement-
Play Games

我們的 API 之前是一個單體應用,各個模塊的服務是通過 Assembly 集成在一起,最後部署在一個 web server 下的。 我們已經在拆分服務並且在 [Ocelot](https://github.com/ThreeMammals/Ocelot) 的基礎上封裝了我們自己的網關,但是服務還... ...


讓 Ocelot 與 asp.net core “共存”

Intro

我們的 API 之前是一個單體應用,各個模塊的服務是通過 Assembly 集成在一起,最後部署在一個 web server 下的。

我們已經在拆分服務並且在 Ocelot 的基礎上封裝了我們自己的網關,但是服務還沒有完全拆分,於是有這麼一個需求,對於 Ocelot 配置的路由去交給 Ocelot 去轉發到真正的服務地址,而那些 Ocelot 沒有定義的路由則讓交給 AspNetCore 去處理。

實現原理

實現原理是讓 Ocelot 作為一個動態分支路由,只有當 Ocelot 配置了對應路由的下游地址才走 Ocelot 的分支,才把請求交給 Ocelot 處理。

我們可以使用 MapWhen 來處理,接下來就需要知道怎麼樣判斷 Ocelot 是否配置了某一個路由,Ocelot 內部的處理管道,在向下游請求之前是要找到對應匹配的下游路由,所以我們去看一看 Ocelot 的源碼,看看 Ocelot 內部是怎麼找下游路由的,Ocelot 找下游路由中間件源碼

        public async Task Invoke(DownstreamContext context)
        {
            var upstreamUrlPath = context.HttpContext.Request.Path.ToString();

            var upstreamQueryString = context.HttpContext.Request.QueryString.ToString();

            var upstreamHost = context.HttpContext.Request.Headers["Host"];

            Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");

            var provider = _factory.Get(context.Configuration);

            // 獲取下游路由
            var downstreamRoute = provider.Get(upstreamUrlPath, upstreamQueryString, context.HttpContext.Request.Method, context.Configuration, upstreamHost);

            if (downstreamRoute.IsError)
            {
                Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");

                SetPipelineError(context, downstreamRoute.Errors);
                return;
            }            
            
            var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
            
            Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");

            context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;

            await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next);
        }

通過上面的源碼,我們就可以判斷 Ocelot 是否有與請求相匹配的下游路由信息

實現

既然找到了 Ocelot 如何找下游路由,就先給 Ocelot 加一個擴展吧,實現代碼如下,Ocelot 擴展完整代碼

        public static IApplicationBuilder UseOcelotWhenRouteMatch(this IApplicationBuilder app,
            Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction)
            => UseOcelotWhenRouteMatch(app, builderAction, new OcelotPipelineConfiguration());

        public static IApplicationBuilder UseOcelotWhenRouteMatch(this IApplicationBuilder app,
            Action<OcelotPipelineConfiguration> pipelineConfigurationAction,
            Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction)
        {
            var pipelineConfiguration = new OcelotPipelineConfiguration();
            pipelineConfigurationAction?.Invoke(pipelineConfiguration);
            return UseOcelotWhenRouteMatch(app, builderAction, pipelineConfiguration);
        }

        public static IApplicationBuilder UseOcelotWhenRouteMatch(this IApplicationBuilder app, Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction, OcelotPipelineConfiguration configuration)
        {
            app.MapWhen(context =>
            {
                // 獲取 OcelotConfiguration
                var internalConfigurationResponse =
                    context.RequestServices.GetRequiredService<IInternalConfigurationRepository>().Get();
                if (internalConfigurationResponse.IsError || internalConfigurationResponse.Data.ReRoutes.Count == 0)
                {
                    // 如果沒有配置路由信息,不符合分支路由的條件,直接退出
                    return false;
                }

                var internalConfiguration = internalConfigurationResponse.Data;
                var downstreamRouteFinder = context.RequestServices
                    .GetRequiredService<IDownstreamRouteProviderFactory>()
                    .Get(internalConfiguration);
                // 根據請求以及上面獲取的Ocelot配置獲取下游路由
                var response = downstreamRouteFinder.Get(context.Request.Path, context.Request.QueryString.ToString(),
                    context.Request.Method, internalConfiguration, context.Request.Host.ToString());
                // 如果有匹配路由則滿足該分支路由的條件,交給 Ocelot 處理
                return !response.IsError
                       && !string.IsNullOrEmpty(response.Data?.ReRoute?.DownstreamReRoute?.FirstOrDefault()
                           ?.DownstreamScheme);
            }, appBuilder => appBuilder.UseOcelot(builderAction, configuration).Wait());

            return app;
        }

使用

在 Startup 里

ConfigurationServices 配置 mvc 和 Ocelot

Configure 方法里配置 ocelot 和 mvc


app.UseOcelotWhenRouteMatch((ocelotBuilder, pipelineConfiguration) =>
                            {
                                // This is registered to catch any global exceptions that are not handled
                                // It also sets the Request Id if anything is set globally
                                ocelotBuilder.UseExceptionHandlerMiddleware();
                                // This is registered first so it can catch any errors and issue an appropriate response
                                ocelotBuilder.UseResponderMiddleware();
                                ocelotBuilder.UseDownstreamRouteFinderMiddleware();
                                ocelotBuilder.UseDownstreamRequestInitialiser();
                                ocelotBuilder.UseRequestIdMiddleware();
                                ocelotBuilder.UseMiddleware<ClaimsToHeadersMiddleware>();
                                ocelotBuilder.UseLoadBalancingMiddleware();
                                ocelotBuilder.UseDownstreamUrlCreatorMiddleware();
                                ocelotBuilder.UseOutputCacheMiddleware();
                                ocelotBuilder.UseMiddleware<HttpRequesterMiddleware>();
                                // cors headers
                                ocelotBuilder.UseMiddleware<CorsMiddleware>();
                            });

app.UseMvc();

新建一個 TestController

    [Route("/api/[controller]")]
    public class TestController : ControllerBase
    {
        public IActionResult Get()
        {
            return Ok(new
            {
                Tick = DateTime.UtcNow.Ticks,
                Msg = "Hello Ocelot",
            });
        }
    }

具體代碼可以參考這個 網關示例項目

示例項目的 Ocelot 配置是存在 Redis 裡面的,配置的 ReRoutes 如下:

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api.php?key=free&appid=0&msg={everything}",
      "UpstreamPathTemplate": "/api/chat/{everything}",
      "UpstreamHttpMethod": [
        "Get",
        "POST",
        "PUT",
        "PATCH",
        "DELETE",
        "OPTIONS"
      ],
      "AddHeadersToRequest": {
      },
      "RequestIdKey": "RequestId",
      "ReRouteIsCaseSensitive": false,
      "ServiceName": "",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "api.qingyunke.com",
          "Port": 80
        }
      ],
      "DangerousAcceptAnyServerCertificateValidator": false
    }
  ],
  "GlobalConfiguration": {
      "HttpHandlerOptions": {
        "AllowAutoRedirect": false,
        "UseCookieContainer": false,
        "UseTracing": false
      }
  }
}

運行項目進行測試:

訪問 Ocelot 定義的路由 http://localhost:65125/api/chat/hello ,返回信息如圖所示:

ocelot-forward-route.png

訪問 Mvc 定義的路由 http://localhost:65125/api/test,返回信息如圖所示:

mvc-roite

上面正常的返回就表示我們的 Ocelot 和 Mvc 同時工作了~

Reference


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在微服務項目中,一個系統可以分割成很多個不同的服務模塊,不同模塊之間我們通常需要進行相互調用。springcloud中可以使用RestTemplate+Ribbon和Feign來調用(工作中基本都是使用feign)。有時為了提高系統的健壯性,某些訪問量大的服務模塊還會做集群部署。但是服務之間的調用不 ...
  • 1 public class Demo { 2 3 public static void main(String[] args) { 4 5 /* 6 * 求出1~100之間,既是3又是7的倍數的自然數出現的次數 7 */ 8 int count = 0; // 計數 9 for (... ...
  • 昨天在使用VS通過ODT連接資料庫扒模型的時候發現了這個異常。經過測試,發現這個異常是因為 ODT 插件無法識別服務名中的“.”字元 導致的,比如“orcl.asian.com”。其他不包含“.”字元的服務名皆可正常連接。做了下簡單的回溯,一個月前的ODT插件是正常工作的,期間資料庫未作任何改動。唯 ...
  • 線段式佈局 有時候需要實現下麵類型的佈局方案,不知道有沒有約定俗成的稱呼,我個人強名為線段式佈局。因為元素恰好放置線上段的端點上。 實現 WPF所有佈局控制項都直接或間接的繼承自System.Windows.Controls. Panel,常用的佈局控制項有Canvas、DockPanel、Grid、S ...
  • 問題一:如何不讓WebBrowser中彈出“安全警告” 當鏈接https網址時,IE會自動彈出上圖中的視窗。 關閉視窗的具體思路如下: 使用WebBrowser載入中/載入完畢後觸發的事件處理程式,在處理程式中查找“安全警告”視窗,併在找到視窗後自動點擊“是”或“否”來關閉視窗。 具體方法是: 1. ...
  • 什麼是 I2C 匯流排 I2C 匯流排(Inter Integrated Circuit Bus)是設備與設備間通信方式的一種。它是一種串列通信匯流排,由飛利浦公司在1980年代為了讓主板、嵌入式系統或手機用以連接低速周邊設備而發展 "[1]" 。I2C 匯流排包含兩根信號線,一根為信號線 SDA ,另一根 ...
  • Ocelot(三) 服務發現 作者:markjiang7m2 原文地址: 源碼地址:https://gitee.com/Sevenm2/OcelotDemo 本文是我關於Ocelot系列文章的第三篇,主要是給大家介紹Ocelot的另一功能。與其說是給大家介紹,不如說是我們一起來共同探討,因為我也是在 ...
  • 自己的意識中一個好的可持續性項目應具備一些特點: 項目在能夠實現業務需要的前提下越簡單越好,特別是項目前期不過度設計,慢慢迭代重構; 有一套完善的服務端數據驗證機制(非前端類似JS校驗),可以相信入庫的數據就是符合邏輯的,並且要易於維護; 可以做但並非必須的分散式部署; 可實現多版本資料庫的支持並且 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...