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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

ASP.NET Core 中间件

發(fā)布時(shí)間:2023/12/4 asp.net 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core 中间件 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1.前言

中間件(middleware)是一種裝配到應(yīng)用管道以處理請(qǐng)求和響應(yīng)的組件。每個(gè)組件:
可選擇是否將請(qǐng)求傳遞到管道中的下一個(gè)組件。
可在管道中的下一個(gè)組件前后執(zhí)行工作。
請(qǐng)求委托(request delegates)用于建立請(qǐng)求管道(request pipeline),請(qǐng)求委托處理每個(gè)HTTP請(qǐng)求。
請(qǐng)求委托通過(guò)使用IApplicationBuilder類型的Run、Map和Use擴(kuò)展方法來(lái)配置,并在Strartup類中傳給Configure方法。每個(gè)單獨(dú)的請(qǐng)求委托都可以被指定為一個(gè)內(nèi)嵌匿名方法(稱為并行中間件,in-line middleware),或者其定義在一個(gè)可重用的類中。這些可重用的類被稱作“中間件”或“中間件組件”。請(qǐng)求管道中的每個(gè)中間件組件負(fù)責(zé)調(diào)用管道中的下一個(gè)組件,或使管道短路。當(dāng)中間件短路時(shí),它被稱為“終端中間件”(terminal middleware),因?yàn)樗柚怪虚g件進(jìn)一步處理請(qǐng)求。

2.使用 IApplicationBuilder 創(chuàng)建中間件管道

ASP.NET Core請(qǐng)求管道包含一系列請(qǐng)求委托,依次調(diào)用。下圖演示了這一概念。沿黑色箭頭執(zhí)行。

每個(gè)委托(中間件)均可在下一個(gè)委托前后執(zhí)行操作。任何委托都能選擇停止傳遞到下一個(gè)委托,轉(zhuǎn)而自己處理該請(qǐng)求,這就是請(qǐng)求管道的短路(下面會(huì)舉例說(shuō)明)。而且是一種有意義的設(shè)計(jì),因?yàn)樗梢员苊獠槐匾墓ぷ鳌1热?#xff0c;一個(gè)授權(quán)(authorization)中間件只有通過(guò)身份驗(yàn)證之后才能調(diào)用下一個(gè)委托,否則它就會(huì)被短路,并返回“Not Authorized”的響應(yīng)。所以應(yīng)盡早在管道中調(diào)用異常處理委托,這樣它們就能捕獲在管道的后期階段發(fā)生的異常。
現(xiàn)在我們來(lái)演示下用一個(gè)簡(jiǎn)單的ASP.NET Core應(yīng)用程序建立單個(gè)請(qǐng)求委托處理每個(gè)HTTP請(qǐng)求(這種情況不包括實(shí)際請(qǐng)求管道):

public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Run(
async context =>
{
await context.Response.WriteAsync("Hello, World!");
});
}
}

響應(yīng)結(jié)果:

由上面我們可以看到,運(yùn)行時(shí)輸出的是Run委托消息,然后我們?cè)俣x多一個(gè)請(qǐng)求委托看看效果,請(qǐng)看如下代碼:

public void Configure(IApplicationBuilder app)
{
//第一個(gè)委托Run
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});
//第二個(gè)委托Run
app.Run(async context =>
{
await context.Response.WriteAsync("Hey, World!");
});
}

響應(yīng)結(jié)果:

由上述代碼可以看到,我們定義兩個(gè)Run委托,但是運(yùn)行第一個(gè)Run委托的時(shí)候就已經(jīng)終止了管道,這是為什么呢?
因?yàn)镽un方法又稱為短路管道(它不會(huì)調(diào)用next請(qǐng)求委托)。因此,Run方法一般在管道尾部被調(diào)用。Run是一種約定,有些中間件組件可能會(huì)暴露他們自己的Run方法,而這些方法只能在管道末尾處運(yùn)行。

讓我們?cè)賮?lái)看看如下代碼:

public void Configure(IApplicationBuilder app)
{
app.Use(
async (context, next) =>
{
context.Response.ContentType
= "text/plain; charset=utf-8";
await context.Response.WriteAsync("進(jìn)入第一個(gè)委托 執(zhí)行下一個(gè)委托之前\r\n");
//調(diào)用管道中的下一個(gè)委托
await next.Invoke();
await context.Response.WriteAsync("結(jié)束第一個(gè)委托 執(zhí)行下一個(gè)委托之后\r\n");
});
app.Run(
async context =>
{
await context.Response.WriteAsync("進(jìn)入第二個(gè)委托\(zhòng)r\n");
await context.Response.WriteAsync("Hello from 2nd delegate.\r\n");
await context.Response.WriteAsync("結(jié)束第二個(gè)委托\(zhòng)r\n");
});
}

響應(yīng)結(jié)果:

通過(guò)響應(yīng)結(jié)果,我們可以看到Use方法將多個(gè)請(qǐng)求委托鏈接在一起。而next參數(shù)表示管道中的下一個(gè)委托。可通過(guò)不調(diào)用next 參數(shù)使管道短路,通常可在下一個(gè)委托前后執(zhí)行操作。

3.順序

向Startup.Configure方法添加中間件組件的順序定義了在請(qǐng)求上調(diào)用它們的順序,以及響應(yīng)的相反順序。此排序?qū)τ诎踩浴⑿阅芎凸δ苤陵P(guān)重要。
以下Startup.Configure方法將為常見應(yīng)用方案添加中間件組件:
●異常/錯(cuò)誤處理(Exception/error handling)
●HTTP嚴(yán)格傳輸安全協(xié)議(HTTP Strict Transport Security Protocol)
●HTTPS重定向(HTTPS redirection)
●靜態(tài)文件服務(wù)器(Static file server)
●Cookie策略實(shí)施(Cookie policy enforcement)
●身份驗(yàn)證(Authentication)
●會(huì)話(Session)
●MVC
請(qǐng)看如下代碼:

從上述示例代碼中,每個(gè)中間件擴(kuò)展方法都通過(guò)Microsoft.AspNetCore.Builder命名空間在 IApplicationBuilder上公開。但是為什么我們要按照這個(gè)順序去添加中間件組件呢?下面我們挑幾個(gè)中間件來(lái)了解下。
UseExceptionHandler(異常/錯(cuò)誤處理)是添加到管道的第一個(gè)中間件組件。因此我們可以捕獲在應(yīng)用程序調(diào)用中發(fā)生的任何異常。那為什么要將異常/錯(cuò)誤處理放在第一位呢?那是因?yàn)檫@樣我們就不用擔(dān)心因前面中間件短路而導(dǎo)致捕獲不到整個(gè)應(yīng)用程序所有異常信息。
UseStaticFiles(靜態(tài)文件)中間件在管道中提前調(diào)用,方便它可以處理請(qǐng)求和短路,而無(wú)需通過(guò)剩余中間組件。也就是說(shuō)靜態(tài)文件中間件不用經(jīng)過(guò)UseAuthentication(身份驗(yàn)證)檢查就可以直接訪問(wèn),即可公開訪問(wèn)由靜態(tài)文件中間件服務(wù)的任何文件,包括wwwroot下的文件。
UseAuthentication(身份驗(yàn)證)僅在MVC選擇特定的Razor頁(yè)面或Controller和Action之后才會(huì)發(fā)生。
經(jīng)過(guò)上面描述,大家都了解中間件順序的重要性了吧。以下示例演示中間件的排序,其中靜態(tài)文件的請(qǐng)求在響應(yīng)壓縮中間件之前由靜態(tài)文件中間件進(jìn)行處理。靜態(tài)文件不會(huì)按照中間件的順序進(jìn)行壓縮。可以壓縮來(lái)自 UseMvcWithDefaultRoute的 MVC 響應(yīng)。示例:

public void Configure(IApplicationBuilder app)
{
// Static files not compressed by Static File Middleware.
app.UseStaticFiles();
app.UseResponseCompression();
app.UseMvcWithDefaultRoute();
}

4.Use、Run和Map方法

你可以使用Use、Run和Map配置HTTP管道。
●Use:Use方法可使管道短路(即不調(diào)用 next 請(qǐng)求委托)。第二節(jié)點(diǎn)有示例代碼演示。
●Run:Run是一種約定,并且某些中間件組件可公開在管道末尾運(yùn)行的Run[Middleware]方法。第二節(jié)點(diǎn)有示例代碼演示。
●Map:Map擴(kuò)展用作創(chuàng)建管道分支。Map*給請(qǐng)求路徑的匹配項(xiàng)來(lái)創(chuàng)建請(qǐng)求管道分支。如果請(qǐng)求路徑以給自定義路徑開頭,則執(zhí)行分支。
下面我們來(lái)看看這段代碼:

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>");
});
}
}

下面表格使用前面的代碼顯示來(lái)自http://localhost:5001的請(qǐng)求和響應(yīng)。

請(qǐng)求

響應(yīng)

localhost:5001

Hello from non-Map delegate.

localhost:5001/map1

Map Test 1

localhost:5001/map2

Map Test 2

localhost:5001/map3

Hello from non-Map delegate.

由上面可以了解到當(dāng)使用Map方法時(shí),將從HttpRequest.Path中刪除匹配的路徑段,并針對(duì)每個(gè)請(qǐng)求將該路徑追加到HttpRequest.PathBase。
MapWhen基于給定謂詞的結(jié)果創(chuàng)建請(qǐng)求管道分支。Func<HttpContext, bool>類型的任何謂詞均可用于將請(qǐng)求映射到管道的新分支(HandleBranch)。在以下示例中,謂詞用于檢測(cè)查詢字符串變量branch是否存在:

public class Startup
{
private static void HandleBranch(IApplicationBuilder app)
{
app.Run(
async context =>
{
var branchVer = context.Request.Query["branch"];
await context.Response.WriteAsync($"Branch used = {branchVer}");
});
}
public void Configure(IApplicationBuilder app)
{
app.MapWhen(context
=> context.Request.Query.ContainsKey("branch"),
HandleBranch);
app.Run(
async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});
}
}

下面表格使用前面的代碼顯示來(lái)自http://localhost:5001的請(qǐng)求和響應(yīng)。

請(qǐng)求

響應(yīng)

http://localhost:5001

Hello from non-Map delegate. <p>

https://localhost:5001/?branch=master

Branch used = master

Map支持嵌套,例如:

public void Configure(IApplicationBuilder app)
{
app.Map(
"/level1", level1App => {
level1App.Map(
"/level2a", level2AApp => {
// "/level1/level2a" processing
});
level1App.Map(
"/level2b", level2BApp => {
// "/level1/level2b" processing
});
});
}

此外Map 還可同時(shí)匹配多個(gè)段:

public class Startup
{
private static void HandleMultiSeg(IApplicationBuilder app)
{
app.Run(
async context =>
{
await context.Response.WriteAsync("Map multiple segments.");
});
}
public void Configure(IApplicationBuilder app)
{
app.Map(
"/map1/seg1", HandleMultiSeg);
app.Run(
async context =>
{
await context.Response.WriteAsync("Hello from non-Map delegate.");
});
}
}

5.編寫中間件(重點(diǎn))

雖然ASP.NET Core為我們提供了一組豐富的內(nèi)置中間件組件,但在某些情況下,你可能需要寫入自定義中間件。

5.1中間件類

通常,中間件應(yīng)該封裝在自定義類中,并且通過(guò)擴(kuò)展方法公開。
下面我們自定義一個(gè)查詢當(dāng)前區(qū)域性的中間件:

public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use((context, next)
=>
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture
= culture;
CultureInfo.CurrentUICulture
= culture;
}
// Call the next delegate/middleware in the pipeline
return next();
});
app.Run(
async (context) =>
{
await context.Response.WriteAsync(
$
"Hello {CultureInfo.CurrentCulture.DisplayName}");
});
}
}

可通過(guò)傳入?yún)^(qū)域性參數(shù)測(cè)試該中間件。例如 http://localhost:7997/?culture=zh、http://localhost:7997/?culture=en。
但是為了更好管理代碼,我們應(yīng)該把委托函數(shù)移到自定義類去:

//自定義RequestCultureMiddleware類
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next
= next;
}
public async Task InvokeAsync(HttpContext context)
{
context.Response.ContentType
= "text/plain; charset=utf-8";
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture
= culture;
CultureInfo.CurrentUICulture
= culture;
}
// Call the next delegate/middleware in the pipeline
await _next(context);
}
}

5.2中間件擴(kuò)展方法

中間件擴(kuò)展方法可以通過(guò)IApplicationBuilder公開中間件。示例創(chuàng)建一個(gè)RequestCultureMiddlewareExtensions擴(kuò)展類并通過(guò)IApplicationBuilder公開:

public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}

再通過(guò)Startup.Configure方法調(diào)用中間件:

public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseRequestCulture();
app.Run(
async (context) =>
{
await context.Response.WriteAsync(
$
"Hello {CultureInfo.CurrentCulture.DisplayName}");
});
}
}

響應(yīng)結(jié)果:

由此整個(gè)自定義ASP.NET Core中間件完成。

6.按請(qǐng)求依賴項(xiàng)

因?yàn)橹虚g件是在應(yīng)用程序啟動(dòng)時(shí)構(gòu)建的,而不是每個(gè)請(qǐng)求時(shí)構(gòu)建,所以在每個(gè)請(qǐng)求期間,中間件構(gòu)造函數(shù)使用的范圍內(nèi)生命周期服務(wù)不與其他依賴關(guān)系注入類型共享。如果您必須在中間件和其他類型之間共享作用域服務(wù),請(qǐng)將這些服務(wù)添加到Invoke方法的簽名中。Invoke方法可以接受由依賴注入(DI)填充的其他參數(shù)。示例:

public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next
= next;
}
// IMyScopedService is injected into Invoke
public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty(
1000);
await _next(httpContext);
}
}
public static class CustomMiddlewareExtensions
{
public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleware>();
}
}
public interface IMyScopedService
{
void MyProperty(decimal input);
}
public class MyScopedService : IMyScopedService
{
public void MyProperty(decimal input)
{
Console.WriteLine(
"MyProperty is " + input);
}
}
public void ConfigureServices(IServiceCollection services)
{
//注入DI服務(wù)
services.AddScoped<IMyScopedService, MyScopedService>();
}

響應(yīng)結(jié)果:

參考文獻(xiàn):
ASP.NET Core中間件
寫入自定義ASP.NET Core中間件

原文地址:https://www.cnblogs.com/wzk153/p/10904988.html

.NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總?http://www.csharpkit.com?

總結(jié)

以上是生活随笔為你收集整理的ASP.NET Core 中间件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。