歡迎光臨
每天分享高質量文章

.net core 中介軟體管道底層剖析

.net core 管道(Pipeline)是什麼?

由上圖可以看出,.net core 管道是請求抵達伺服器到響應結果傳回的中間的一系列的處理過程,如果我們簡化一下成下圖來看的話,.net core 的管道其實就是中介軟體的部分。微軟中介軟體檔案

為什麼管道就是中介軟體的部分了呢?我是這麼理解的,.net core 是透過Startup 類配置服務和應用的請求管道,所以狹義點來講這個管道就是指的請求管道,就是我們今天要理解的中介軟體管道。

.net core 核心體系結構的特點就是一個中介軟體系統,它是處理請求和響應的程式碼段。中介軟體彼此連結,形成一個管道。傳入的請求透過管道傳遞,其中每個中介軟體都有機會在將它們傳遞到下一個中介軟體之前對它們進行處理。傳出響應也以相反的順序透過管道傳遞。

PS:簡單來講就是請求開始到響應結束的中間的一大部分。你可以理解成 ” 汽車銷售 ” (開始買車到提車的過程,但願不會坐在賓士車蓋上哭),哈哈……

還有我們來看看,為什麼我們要簡化來看,在執行時 .net core 會預先註入一些必要的服務及依賴項,預設註入(ServiceCollection)的服務清單如下:

我們先斷章取義地看,這裡面有 Kestrel 處理請求,將接收到的請求內容(字串流)轉化成結構化的資料(HttpContext)供後面的中介軟體使用的服務。欸,服務喲。那其實也就是 Kestrel 服務也是中介軟體嘛。

而第一張圖中的MVC本身也作為中介軟體來實現的。

還有一些相關的服務都可以看看上面截圖的服務,而且旁邊標註的生命週期的型別就是之前所講到的 .net core 的三種註入樣式 。

那麼從程式入口來講,過程是怎麼樣的呢?

從應用程式主入口 Main() –> WebHost –> UseStartup

/// 
/// Specify the startup type to be used by the web host.
/// 

/// The to configure.
/// The to be used.
/// The .
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
{
string name = startupType.GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name).ConfigureServices((Action)delegate(IServiceCollection services)
{
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
{
ServiceCollectionServiceExtensions.AddSingleton(services,
typeof(IStartup), startupType);
}
else
{
ServiceCollectionServiceExtensions.AddSingleton(services,
typeof(IStartup), (Funcobject>)delegate(IServiceProvider sp)
{
IHostingEnvironment requiredService
= ServiceProviderServiceExtensions.GetRequiredService(sp);
return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.get_EnvironmentName()));
});
}
});
}

上面的程式碼就可以解釋說,會預先註入的必要的服務,在透過委託的方式,註入 Startup 裡的服務。具體可以繼續探究:

UseSetting:Add or replace a setting in the configuration.
/// 
/// Add or replace a setting in the configuration.
/// 

/// The key of the setting to add or replace.
/// The value of the setting to add or replace.
/// The .
public IWebHostBuilder UseSetting(string key, string value)
{
_config.set_Item(key, value);
return this;
}

ConfigureServices:Adds a delegate for configuring additional services for the host or web application. This may be called multiple times.
/// 
/// Adds a delegate for configuring additional services for the host or web application. This may be called
/// multiple times.
/// 

/// A delegate for configuring the .
/// The .
public IWebHostBuilder ConfigureServices(Action configureServices)
{
if (configureServices == null)
{
throw new ArgumentNullException(configureServices);
}
return ConfigureServices(delegate(WebHostBuilderContext _, IServiceCollection services)
{
configureServices(services);
});
}

  ConventionBasedStartup

public class ConventionBasedStartup : IStartup
{
    private readonly StartupMethods _methods;

    public ConventionBasedStartup(StartupMethods methods)
    {
        _methods = methods;
    }

    public void Configure(IApplicationBuilder app)
    {
        try
        {
            _methods.ConfigureDelegate(app);
        }
        catch (Exception ex)
        {
            if (ex is TargetInvocationException)
            {
                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
            }
            throw;
        }
    }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        try
        {
            return _methods.ConfigureServicesDelegate(services);
        }
        catch (Exception ex)
        {
            if (ex is TargetInvocationException)
            {
                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
            }
            throw;
        }
    }
}

 OK,到這裡就已經確定了,我們可控的是透過Startup註入我們所需的服務,就是Startup註入的中介軟體可以做所有的事情,如處理認證,錯誤,靜態檔案等等,並且如上面所說的 MVC 在 .net core 也是作為中介軟體實現的。

那麼 .net core 給我們內建了多少中介軟體呢?如下圖:

我們很經常用到的內建中介軟體有:

    app.UseExceptionHandler(); //異常處理
    app.UseStaticFiles(); //靜態檔案
    app.UseAuthentication(); //Auth驗證
    app.UseMvc(); //MVC

我們知道可以在啟動類的 Configure 方法中配置 .net core 管道,透過呼叫 IApplicationBuilder 上的 Use*** 方法,就可以向管道新增一個中介軟體,被新增的順序決定了請求遍歷它們的順序。因此,如上面新增內建中介軟體的順序,傳入的請求將首先遍歷異常處理程式中介軟體,然後是靜態檔案中介軟體,然後是身份驗證中介軟體,最終將由MVC中介軟體處理。

Use*** 方法實際上只是 .net core 提供給我們的“快捷方式”,以便更容易地構建管道。在幕後,它們最終都使用(直接或間接)這些關鍵字:Use 和 Run 。兩者都向管道中添加了一個中介軟體,不同之處在於Run添加了一個終端中介軟體,即管道中的最後一個中介軟體。

那麼有內建,就應該可以定製的。如何定製自己的中介軟體呢?上一些簡陋的demo演示一下:

(1)無分支管道

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{

       // Middleware A
       app.Use(async (context, next) =>
       {
            Console.WriteLine("A (before)");
            await next();
            Console.WriteLine("A (after)");
       });

       // Middleware B
       app.Use(async (context, next) =>
       {
            Console.WriteLine("B (before)");
            await next();
            Console.WriteLine("B (after)");
       });

       // Middleware C (terminal)
       app.Run(async context =>
       {
            Console.WriteLine("C");
            await context.Response.WriteAsync("Hello world");
       });

}

列印結果:

A (before)
B (before)
C
B (after)
A (after)

那用管道圖展示的話就是:

(2)有分支管道,當使用無分支的管道時,相當於就是一條線直走到底再傳迴響應結果。但一般情況下,我們都希望管道更具靈活性。建立有分支的管道就需要使用到 Map 擴充套件用作約定來建立管道分支。Map 是基於給定請求路徑的匹配項來建立請求管道分支的,如果請求路徑以給定的路徑開頭,就執行分支。那麼就有兩種型別有分支的管道:

1.無連結分支,上官方demo:

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.

 

");
        });
    }
}

結果:

以上無連結分支很容易就理解了,就是不同的路徑跑不同的分支。如果是有引數匹配的話,就要使用 MapWhen,而 MapWhen 基於給定謂詞的結果建立請求管道分支。Func 型別的任何謂詞均可用於將請求對映到管道的新分支。謂詞用於檢測查詢字串變數 branch 是否存在。

2.有連結(重新連線上主管道)分支,建立有連結分支管道就要使用到 UseWhen,上demo:

public void Configure(IApplicationBuilder app)
{
    app.Use(async (context, next) =>
    {
        Console.WriteLine("A (before)");
        await next();
        Console.WriteLine("A (after)");
    });

    app.UseWhen(
        context => context.Request.Path.StartsWithSegments(new PathString("/foo")),
        a => a.Use(async (context, next) =>
        {
            Console.WriteLine("B (before)");
            await next();
            Console.WriteLine("B (after)");
        }));

    app.Run(async context =>
    {
        Console.WriteLine("C");
        await context.Response.WriteAsync("Hello world");
    });
}

像上面的程式碼,當請求不是以 ” /foo ” 開頭的時候,結果為:

當請求是以 ” /foo ” 開頭的時候,結果為:

A (before)
B (before)
C
B (after)
A (after)

         正如您所看到的,中介軟體管道背後的思想非常簡單,但是非常強大。大多數功能都是 .net core(身份驗證、靜態檔案、快取、MVC等)作為中介軟體實現。當然,編寫自己的程式碼也很容易!

        後面可以進階寫自己的中介軟體,官方檔案。

原文地址:https://www.cnblogs.com/Vam8023/p/10700254.html

已同步到看一看
贊(0)

分享創造快樂