日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

.net core 中间件管道底层剖析

發布時間:2023/12/4 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .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?

/// <summary>
/// Specify the startup type to be used by the web host.
/// </summary>
/// <param name="hostBuilder">The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" /> to configure.</param>
/// <param name="startupType">The <see cref="T:System.Type" /> to be used.</param>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
{
string name = startupType.GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name).ConfigureServices((Action<IServiceCollection>)delegate(IServiceCollection services)
{
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
{
ServiceCollectionServiceExtensions.AddSingleton(services,
typeof(IStartup), startupType);
}
else
{
ServiceCollectionServiceExtensions.AddSingleton(services,
typeof(IStartup), (Func<IServiceProvider, object>)delegate(IServiceProvider sp)
{
IHostingEnvironment requiredService
= ServiceProviderServiceExtensions.GetRequiredService<IHostingEnvironment>(sp);
return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.get_EnvironmentName()));
});
}
});
}

上面的代碼就可以解釋說,會預先注入的必要的服務,在通過委托的方式,注入 Startup 里的服務。具體可以繼續探究:

UseSetting:Add or replace a setting in the configuration./// <summary>
/// Add or replace a setting in the configuration.
/// </summary>
/// <param name="key">The key of the setting to add or replace.</param>
/// <param name="value">The value of the setting to add or replace.</param>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
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.
/// <summary>
/// Adds a delegate for configuring additional services for the host or web application. This may be called
/// multiple times.
/// </summary>
/// <param name="configureServices">A delegate for configuring the <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection" />.</param>
/// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
public IWebHostBuilder ConfigureServices(Action<IServiceCollection> 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. <p>");
});
}
}

結果:

以上無連結分支很容易就理解了,就是不同的路徑跑不同的分支。如果是有參數匹配的話,就要使用 MapWhen,而 MapWhen 基于給定謂詞的結果創建請求管道分支。Func<HttpContext, bool> 類型的任何謂詞均可用于將請求映射到管道的新分支。謂詞用于檢測查詢字符串變量?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

.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?

總結

以上是生活随笔為你收集整理的.net core 中间件管道底层剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。