.NET Core基礎篇之:白話管道中介軟體

暢飲無緒發表於2021-12-22

.Net Core中,管道往往伴隨著請求一起出現。客戶端發起Http請求,服務端去響應這個請求,之間的過程都在管道內進行。

舉一個生活中比較常見的例子:旅遊景區

我們都知道,有些景區大門離景區很遠,我們需要經過層層關卡才能到達景區。

我的請求最終就是去到景區,去到景區的整個過程就是管道,景區就是伺服器,層層關卡就是一個個中介軟體了,比如:門票停車費擺渡費等等。

如果其中任何一個中介軟體卡殼了,比如我沒買門票,那別人肯定是不讓我進去,這就是管道短路了。


.NET Core 請求管道包含一系列Http請求委託(RequestDelegate),依次呼叫。

微軟給的圖示:

image

.Net Core服務

在解釋管道的使用方法之前,我們先來準備一個.Net Core服務。

建立一個.Net Core控制檯應用程式,並實現如下程式碼,一個簡單的使用 Kestrel 託管的服務就完成了:

internal class Program
{
    static void Main(string[] args)
    {
        new WebHostBuilder()
            .UseKestrel()
            .UseStartup<Startup2>()
            .Build()
            .Start();

        Console.ReadLine();
    }
}
public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
    }
}

image

這也是.Net Core的優點之一,只選擇我們需要的,摒棄那些多餘的功能。優點是優點,一般開發中也犯不上這樣去做。

Kestrel 託管預設監聽埠:5000

管道中介軟體

微軟這邊內建了三個擴充套件函式供我們構建自己的中介軟體:

  1. Use
  2. Map
  3. Run

其中UseMap函式還提供了對應的分支擴充套件:UseWhenMapWhenUseMiddleware。下面我們一個個來解釋。

app.Use

Use 是最常用的一種模式,承接上一個請求並執行下一個請求的任務

public void Configure(IApplicationBuilder app)
{
	app.Use(async (context, next) =>
	{
		Console.WriteLine("middleware1");
         await next.Invoke();
	});
    app.Use(async (context, next) =>
	{
		Console.WriteLine("middleware2");
	});
}

app.UseWhen

UseWhenUse的基礎上提供了條件分支的功能

app.UseWhen(context =>
	 // 判斷請求路徑的開頭是否是/h
	context.Request.Path.StartsWithSegments(new PathString("/h")),
	c => c.Use(async (context, next) =>
	{
		Console.WriteLine("middleware1");
		await next.Invoke();
	})
);
app.Use(async (context, next) =>
{
	Console.WriteLine("middleware2");
});

image

app.Map

Map我們可以理解成專為請求路徑擴充套件的分支中介軟體,可以根據請求路徑去處理對應分支邏輯,與上面的UseWhen例子效果類似,但更加方便。

app.Map("/h", _app =>
{
	_app.Use(async (context, next) =>
	{
		Console.WriteLine("hello world");
	});
});

app.MapWhen

MapWhenUseWhen類似,都是在請求上下文的基礎上去擴充套件分支,比Map更加靈活。

app.MapWhen(context => { return context.Request.Query["name"] == "tony"; }, _app => {
    _app.Use(async (context, next) => {
        context.Response.ContentType = "text/plain; charset=utf-8";
        await context.Response.WriteAsync("i 服了 you");
    });
});

app.Run

Run一般用於斷路或請求管道的末尾,不會將請求傳遞下去

app.Run(async context =>
{
    await context.Response.WriteAsync("hello world");
});

UseMiddleware

將一個完整的類新增到管道中介軟體,也就是將上面的請求委託,用類以及函式的形式替代了,便於我們的程式碼管理。

app.UseMiddleware<DotnetboyMiddleware>();

public class DotnetboyMiddleware
{
	private readonly RequestDelegate _next;
	private readonly string _name;
	public DotnetboyMiddleware(RequestDelegate next, string name)
	{
		_next = next;
		_name = name;
		}
	public Task Invoke(HttpContext context)
	{
		context.Response.WriteAsync($"my name is {_name}").Wait();
		return this._next(context);
	}
}

微軟內建的一些管道中介軟體擴充套件函式就介紹完了,下面我們實現一下微軟例項圖示中的效果:

public void Configure(IApplicationBuilder app)
{
	app.Use(async (context, next) =>
	{
		Console.WriteLine("middleware1 : in");
		await next.Invoke();
		Console.WriteLine("middleware1 : out");
	});
	app.Use(async (context, next) =>
	{
		Console.WriteLine("middleware2 : in");
		await next.Invoke();
		Console.WriteLine("middleware2 : out");
	});
	app.Run(async context =>
	{
		Console.WriteLine("Hello World");
		await context.Response.WriteAsync("Hello World");
	});
}

image

image

從上面的例子中我們可以看到,中介軟體都是由上而下依次執行,由每個中介軟體決定是否繼續執行下一個中介軟體,最終到響應結果。

如果哪個中介軟體決定不往下執行,那通道也就短路了,比如我們去掉 middleware2await next.Invoke();

執行到 Console.WriteLine("middleware2 : out"); 就短路了,此路不通,原路返回。

image

因為管道中介軟體執行邏輯的關係,我們在實際開發中要注意兩點:

  • 1、謹慎使用管道短路

  • 2、注意中介軟體的使用順序,比如:路由中介軟體肯定是要在認證中介軟體前面執行,有中介軟體需要訪問檔案,在此之前就必須先執行開放靜態檔案的中介軟體

相關文章