一文說通Dotnet Core的中間件

来源:https://www.cnblogs.com/tiger-wang/archive/2020/06/03/13038419.html
-Advertisement-
Play Games

前幾天,公眾號後臺有朋友在問Core的中間件,所以專門抽時間整理了這樣一篇文章。 一、前言 中間件(Middleware)最初是一個機械上的概念,說的是兩個不同的運動結構中間的連接件。後來這個概念延伸到軟體行業,大家把應用操作系統和電腦硬體之間過渡的軟體或系統稱之為中間件,比方驅動程式,就是一個典型 ...


前幾天,公眾號後臺有朋友在問Core的中間件,所以專門抽時間整理了這樣一篇文章。

一、前言

中間件(Middleware)最初是一個機械上的概念,說的是兩個不同的運動結構中間的連接件。後來這個概念延伸到軟體行業,大家把應用操作系統和電腦硬體之間過渡的軟體或系統稱之為中間件,比方驅動程式,就是一個典型的中間件。再後來,這個概念就泛開了,任何用來連接兩個不同系統的東西,都被叫做中間件。

所以,中間件只是一個名詞,不用太在意,實際代碼跟他這個詞,也沒太大關係。

中間件技術,早在.Net framework時期就有,只不過,那時候它不是Microsoft官方的東西,是一個叫OWIN的三方框架下的實現。到了.Net core,Microsoft才把中間件完整加到框架里來。

感覺上,應該是Core參考了OWIN的中間件技術(猜的,未求證)。在實現方式上,兩個框架下的中間件沒什麼區別。

下麵,我們用一個實際的例子,來理清這個概念。

    為了防止不提供原網址的轉載,特在這裡加上原文鏈接:https://www.cnblogs.com/tiger-wang/p/13038419.html

二、開發環境&基礎工程

這個Demo的開發環境是:Mac + VS Code + Dotnet Core 3.1.2。

$ dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.201
 Commit:    b1768b4ae7

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  10.15
 OS Platform: Darwin
 RID:         osx.10.15-x64
 Base Path:   /usr/local/share/dotnet/sdk/3.1.201/

Host (useful for support):
  Version: 3.1.3
  Commit:  4a9f85e9f8

.NET Core SDKs installed:
  3.1.201 [/usr/local/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

首先,在這個環境下建立工程:

  1. 創建Solution
% dotnet new sln -o demo
The template "Solution File" was created successfully.
  1. 這次,我們用Webapi創建工程
cd demo
% dotnet new webapi -o demo
The template "ASP.NET Core Web API" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on demo/demo.csproj...
  Restore completed in 179.13 ms for demo/demo.csproj.

Restore succeeded.
  1. 把工程加到Solution中
% dotnet sln add demo/demo.csproj

基礎工程搭建完成。

三、創建第一個中間件

我們先看下Demo項目的Startup.cs文件:

namespace demo
{
    public class Startup
    {

        public Startup(IConfiguration configuration)
        
{
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        
{
            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        
{
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

這是Startup預設生成後的樣子(註意,不同的操作系統下生成的代碼會略有不同,但本質上沒區別)。

其中,Configure是中間件的運行定義,ConfigureServices是中間件的參數設置註入。

我們在Configure方法里,加入一個簡單的中間件:

app.UseAuthorization();
/////////////////////下麵是加入的代碼
app.Use(async (context, next) =>
{
    // your code
    await next.Invoke();
    // your code
});
/////////////////////////
app.UseEndpoints(endpoints =>
{
        endpoints.MapControllers();
});

在這個代碼中,app.Use是引入中間件的方式,而真正的中間件,是async (context, next),這是一個delegate方法。

中間件方法的兩個參數,context是上下文HttpContextnext指向下一個中間件。

其中,next參數很重要。中間件採用管道的形式執行。多個中間件,通過next進行調用。

四、中間件的短路

在前一節,我們看到了中間件的標準形式。

有時候,我們希望中間件執行完成後就退出執行,而不進入下一個中間件。這時候,我們可以把await next.Invoke()從代碼中去掉。變成下麵的形式:

app.Use(async (context, next) =>
{
    // your code
});

對於這種形式,Microsoft給出了另一個方式的寫法:

app.Run(async context =>
{
    // your code
});

這兩種形式,效果完全一樣。

這種形式,我們稱之為短路,就是說在這個中間件執行後,程式即返回數據給客戶端,而不執行下麵的中間件。

五、中間件的映射

有時候,我們需要把一個中間件映射到一個Endpoint,用以對外提供簡單的API處理。這種時間,我們需要用到映射:

app.Map("/apiname", apiname => {
    app.Use(async (context, next) =>
    {
        // your code
        await next.Invoke();
    });  
});

此外,映射支持嵌套:

app.Map("/router", router => {
        router.Map("/api1name", api1Name => {
        app.Use(async (context, next) =>
        {
            // your code
            await next.Invoke();
        }); 
        });
      router.Map("/api2name", api2Name => {
        app.Use(async (context, next) =>
        {
            // your code
            await next.Invoke();
        });     
    });
})

對於這兩個嵌套的映射,我們訪問的Endpoint分別是/router/api1name/router/api2name

以上部分,就是中間件的基本內容。

但是,這兒有個問題:為什麼我們從各處文章里看到的中間件,好像都不是這麼寫的?

嗯,答案其實很簡單,我們看到的方式,也都是中間件。只不過,那些代碼,是這個中間件的最基本樣式的變形。

下麵,我們就來說說變形。

六、變形1: 獨立成一個類

大多數情況下,我們希望中間件能獨立成一個類,方便控制,也方便程式編寫。

這時候,我們可以這樣做:先寫一個類:

public class TestMiddleware
{

    private readonly RequestDelegate _next;

    public TestMiddleware(RequestDelegate next)
    
{
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    
{
        // Your code
        await _next.Invoke(context);
    }
}

這個類里contextnext和上面簡單形式下的兩個參數,類型和意義是完全一樣的。

下麵,我們把這個類引入到Configure中:

app.UseMiddleware<TestMiddleware>();

註意,引入Middleware類,需要用app.UseMiddleware而不是app.Use

app.Use引入的是方法,而app.UseMiddleware引入的是類。就這點區別。

如果再想高大上一點,可以做個Extensions:

public static class TestMiddlewareExtensions
{

    public static IApplicationBuilder UseTestMiddleware(this IApplicationBuilder app)
    
{
        return app.UseMiddleware<TestMiddleware>();
    }
}

然後,在Configure中,就可以換成:

app.UseTestMiddleware();

看著高大上了有沒有?

七、變形2: 簡單引入參數

有時候,我們需要給在中間件初始化時,給它傳遞一些參數。

看類:

public class TestMiddleware
{

    private readonly RequestDelegate _next;
    private static object _parameter

    public TestMiddleware(RequestDelegate next, object parameter)
    
{
        _next = next;
        _parameter = parameter;
    }

    public async Task Invoke(HttpContext context)
    
{
        // Your code
        await _next.Invoke(context);
    }
}

那相應的,我們在Configure中引入時,需要寫成:

app.UseMiddleware<TestMiddleware>(new object());

同理,如果我們用Extensions時:

public static class TestMiddlewareExtensions
{

    public static IApplicationBuilder UseTestMiddleware(this IApplicationBuilder app, object parameter)
    
{
        return app.UseMiddleware<TestMiddleware>(parameter);
    }
}

同時,引入變為:

app.UseTestMiddleware(new object());

八、變形3: 依賴註入參數

跟前一節一樣,我們需要引入參數。這一節,我們用另一種更優雅的方式:依賴註入參數。

先創建一個interface:

	   

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

-Advertisement-
Play Games
更多相關文章
  • spring註入map,spring註入一個介面的多個實現類在map里 ...
  • 轉載https://www.jianshu.com/p/5d27888e7c93#!/xh #!/usr/bin/env python # -*- coding=utf-8 -*- """ AES加密解密工具類 @author jzx @date 2018/10/24 此工具類加密解密結果與 htt ...
  • 什麼是sam 轉換 Single Abstract Method 實際上這是java8中提出的概念,你就把他理解為是一個方法的介面的就可以了 看一下我們每天都在使用的線程池 ExecutorService executorService= Executors.newScheduledThreadPo ...
  • 結合 AOP 輕鬆處理事件發佈處理日誌 Intro 前段時間,實現了 EventBus 以及 EventQueue 基於 Event 的事件處理,但是沒有做日誌(EventLog)相關的部分,原本想增加兩個介面, 處理事件發佈日誌和事件處理日誌,最近用了 AOP 的思想處理了 EntityFrame ...
  • Microsoft.AspNetCore.Mvc.Versioning //引入程式集 .net core 下麵api的版本控製作用不需要多說,可以查閱https://www.cnblogs.com/dc20181010/p/11313738.html 普通的版本控制一般是通過鏈接、header此類 ...
  • 系列文章 基於 abp vNext 和 .NET Core 開發博客項目 - 使用 abp cli 搭建項目 基於 abp vNext 和 .NET Core 開發博客項目 - 給項目瘦身,讓它跑起來 基於 abp vNext 和 .NET Core 開發博客項目 - 完善與美化,Swagger登場 ...
  • 引用NModbus 在NuGet搜索NModbus,添加引用。 封裝ModbusTcp類 public class ModbusTCP { private ModbusFactory modbusFactory; private IModbusMaster master; private TcpCl ...
  • 參考文檔: https://www.cnblogs.com/liaods/p/10101513.html https://www.cnblogs.com/zyz-Notes/p/12030281.html 本示例使用MVC項目做演示(不推薦,推薦直接用WebAPI),框架版本使用 4.6.2 為了支 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...