讓 .NET 輕鬆構建中間件模式代碼(二)

来源:https://www.cnblogs.com/weihanli/archive/2020/04/15/12709603.html
-Advertisement-
Play Games

讓 .NET 輕鬆構建中間件模式代碼(二) 支持管道的中斷和分支 Intro 上次實現了一個基本的構建中間件模式的中間件構建器,現在來豐富一下功能,讓它支持中斷和分支,分別對應 asp.net core 中的 和 實現管道中斷 實現中間件的中斷其實很簡單,通過上一次的分析我們已經知道,中間件每一個部 ...


讓 .NET 輕鬆構建中間件模式代碼(二)--- 支持管道的中斷和分支

Intro

上次實現了一個基本的構建中間件模式的中間件構建器,現在來豐富一下功能,讓它支持中斷和分支,分別對應 asp.net core 中的 applicationBuilder.RunapplicationBuilder.MapWhen

實現管道中斷

實現中間件的中斷其實很簡單,通過上一次的分析我們已經知道,中間件每一個部分其實是一個上下文和 next 的委托,只需要忽略 next,不執行 next 就可以了,就可以中斷後面中間件的執行。

定義一個 Run 擴展方法來實現方便的實現中間件中斷:

public static IPipelineBuilder<TContext> Run<TContext>(this IPipelineBuilder<TContext> builder, Action<TContext> handler)
{
    return builder.Use(_ => handler);
}

public static IAsyncPipelineBuilder<TContext> Run<TContext>(this IAsyncPipelineBuilder<TContext> builder, Func<TContext, Task> handler)
{
    return builder.Use(_ => handler);
}

實現分支

分支的實現主要是參考 asp.net core 里 applicationBuilder.Map/applicationBuilder.MapWhen 實現分支路由的做法,在 asp.net core 里,MapWhen 是一個擴展方法,其實現是一個 MapWhenMiddleware,有興趣可以看 asp.net core 的源碼。

實現原理也挺簡單的,其實就是滿足分支的條件時創建一個全新的中間件管道,當滿足條件的時候就就執行這個分支中間件管道,否則就跳過這個分支進入下一個中間件。

首先在 PipelineBuilder 的介面定義中增加了一個 New 方法用來創建一個全新的中間件管道,定義如下:

public interface IPipelineBuilder<TContext>
{
    IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware);

    Action<TContext> Build();

    IPipelineBuilder<TContext> New();
}

//
public interface IAsyncPipelineBuilder<TContext>
{
    IAsyncPipelineBuilder<TContext> Use(Func<Func<TContext, Task>, Func<TContext, Task>> middleware);

    Func<TContext, Task> Build();

    IAsyncPipelineBuilder<TContext> New();
}

實現就是直接創建了一個新的 PipelineBuilder<TContext> 對象,示例如下:

internal class PipelineBuilder<TContext> : IPipelineBuilder<TContext>
{
    private readonly Action<TContext> _completeFunc;
    private readonly List<Func<Action<TContext>, Action<TContext>>> _pipelines = new List<Func<Action<TContext>, Action<TContext>>>();

    public PipelineBuilder(Action<TContext> completeFunc)
    {
        _completeFunc = completeFunc;
    }

    public IPipelineBuilder<TContext> Use(Func<Action<TContext>, Action<TContext>> middleware)
    {
        _pipelines.Add(middleware);
        return this;
    }

    public Action<TContext> Build()
    {
        var request = _completeFunc;

        for (var i = _pipelines.Count - 1; i >= 0; i--)
        {
            var pipeline = _pipelines[i];
            request = pipeline(request);
        }

        return request;
    }

    public IPipelineBuilder<TContext> New() => new PipelineBuilder<TContext>(_completeFunc);
}

非同步的和同步類似,這裡就不再贅述,有疑問可以直接看文末的源碼鏈接

接著就可以定義我們的分支擴展了

public static IPipelineBuilder<TContext> When<TContext>(this IPipelineBuilder<TContext> builder, Func<TContext, bool> predict, Action<IPipelineBuilder<TContext>> configureAction)
{
    return builder.Use((context, next) =>
    {
        if (predict.Invoke(context))
        {
            var branchPipelineBuilder = builder.New();
            configureAction(branchPipelineBuilder);
            var branchPipeline = branchPipelineBuilder.Build();
            branchPipeline.Invoke(context);
        }
        else
        {
            next();
        }
    });
}

使用示例

我們可以使用分支和中斷來改造一下昨天的示例,改造完的示例如下:

var requestContext = new RequestContext()
{
    RequesterName = "Kangkang",
    Hour = 12,
};

var builder = PipelineBuilder.Create<RequestContext>(context =>
        {
            Console.WriteLine($"{context.RequesterName} {context.Hour}h apply failed");
        })
        .When(context => context.Hour <= 2, pipeline =>
                {
                    pipeline.Use((context, next) =>
                    {
                        Console.WriteLine("This should be invoked");
                        next();
                    });
                    pipeline.Run(context => Console.WriteLine("pass 1"));
                    pipeline.Use((context, next) =>
                    {
                        Console.WriteLine("This should not be invoked");
                        next();
                        Console.WriteLine("will this invoke?");
                    });
                })
        .When(context => context.Hour <= 4, pipeline =>
            {
                pipeline.Run(context => Console.WriteLine("pass 2"));
            })
        .When(context => context.Hour <= 6, pipeline =>
            {
                pipeline.Run(context => Console.WriteLine("pass 3"));
            })

    ;
var requestPipeline = builder.Build();
Console.WriteLine();
foreach (var i in Enumerable.Range(1, 8))
{
    Console.WriteLine($"--------- h:{i} apply Pipeline------------------");
    requestContext.Hour = i;
    requestPipeline.Invoke(requestContext);
    Console.WriteLine("----------------------------");
}

輸出結果如下:

看輸出結果我們可以看到 Run 後面註冊的中間件是不會執行的,Run 前面註冊的中間件正常執行

然後定義的 When 分支也是正確執行的~~

Reference


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

-Advertisement-
Play Games
更多相關文章
  • [toc] 1、分析網頁 當我們去爬取網頁時,首先要做的就是先分析網頁結構,然後就會發現相應的規律,如下所示: 生成鏈接:從網頁鏈接的規律中可得寫一個for迴圈即可生成它的鏈接,其中它的間隔為25,程式如下: 得到的結果如下: 2、請求伺服器 在爬取網頁之前,我們要向伺服器發出請求 2.1導入包 沒 ...
  • 前言 文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 PS:如有需要Python學習資料的小伙伴可以加點擊下方鏈接自行獲取http://t.cn/A6Zvjdun 最近找工作,爬蟲面試的一個面試題。涉及的反爬還是比較全面的,結果公 ...
  • 數據分析當中讀取本地數據,txt,excel,csv,json,SQLite,SqLite_json等數據類型 ...
  • 大家在學習java多線程的時候肯定會遇到這個問題,而且在面試的時候也可能會談到java多線程這一塊的知識。今天我們就來看看這個東西~~~ synchronized 這個是對類實例進行加鎖,可以簡稱為“實例鎖”或者是“對象鎖”。當某個線程調用synchronized方法的時候,就會給它加上了一個鎖,其 ...
  • 我的LeetCode:https://leetcode cn.com/u/ituring/ 我的LeetCode刷題源碼[GitHub]:https://github.com/izhoujie/Algorithmcii LeetCode 542. 01 矩陣 題目 給定一個由 0 和 1 組成的矩陣 ...
  • 在控制台模擬操作cmd 我們設計簡單的程式實現以下功能 1.cd顯示當前目錄的名稱或將其更改。 2.date顯示時間 3.md 創建一個目錄 4. rd 刪除目錄 5.dir 顯示一個目錄中的文件和子目錄 6 help 提示操作 代碼 先在項目下創建一個help.txt文件,內容從cmd的help中 ...
  • Contracts Contracts其實就是倡導面向介面編程,來達到解耦的目的。而這些通用的介面已經由Laravel為你設計好了。就是這些Contracts. 那麼Laravel如何知道我們需要使用哪個實現呢? 在Laravel預設的Contracts綁定中,在'Illuminate/Founda ...
  • 【目錄】 一、什麼是元類 二、類是如何產生的——關鍵字class創造類的過程 三、如何自定義元類來控制類的產生 四、內置方法 __call__ 五、自定義元類控制類的調用=》類的對象的產生 六、再訪——屬性查找 一、什麼是元類 一切都源自於一句話:一切皆為對象 # 元類就是用來實例化產生類的類# 關 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...