詳解ASP.NET Core中介軟體Middleware
本文為官方文件譯文,官方文件現已非機器翻譯 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1
什麼是中介軟體(Middleware)?
中介軟體是組裝到應用程式管道中以處理請求和響應的軟體。 每個元件:
- 選擇是否將請求傳遞給管道中的下一個元件。
- 可以在呼叫管道中的下一個元件之前和之後執行工作。
請求委託(Request delegates)用於構建請求管道,處理每個HTTP請求。
請求委託使用Run
,Map
和Use
擴充套件方法進行配置。單獨的請求委託可以以內聯匿名方法(稱為內聯中介軟體)指定,或者可以在可重用的類中定義它。這些可重用的類和內聯匿名方法是中介軟體或中介軟體元件。請求流程中的每個中介軟體元件都負責呼叫流水線中的下一個元件,如果適當,則負責連結短路。
將HTTP模組遷移到中介軟體解釋了ASP.NET Core和以前版本(ASP.NET)中的請求管道之間的區別,並提供了更多的中介軟體示例。
使用 IApplicationBuilder 建立中介軟體管道
ASP.NET Core請求流程由一系列請求委託組成,如下圖所示(執行流程遵循黑色箭頭):
每個委託可以在下一個委託之前和之後執行操作。委託還可以決定不將請求傳遞給下一個委託,這稱為請求管道的短路。短路通常是可取的,因為它避免了不必要的工作。例如,靜態檔案中介軟體可以返回一個靜態檔案的請求,並使管道的其餘部分短路。需要在管道早期呼叫異常處理委託,因此它們可以捕獲後面管道的異常。
最簡單的可能是ASP.NET Core應用程式建立一個請求的委託,處理所有的請求。此案例不包含實際的請求管道。相反,針對每個HTTP請求都呼叫一個匿名方法。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; public class Startup { public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello,World!"); }); } }
第一個 app.Run
委託終止管道。
有如下程式碼:
通過瀏覽器訪問,發現確實在第一個app.Run
終止了管道。
您可以將多個請求委託與app.Use
連線在一起。 next
引數表示管道中的下一個委託。 (請記住,您可以通過不呼叫下一個引數來結束流水線。)通常可以在下一個委託之前和之後執行操作,如下例所示:
public class Startup { public void Configure(IApplicationBuilder app) { app.Use(async (context,next) => { await context.Response.WriteAsync("進入第一個委託 執行下一個委託之前\r\n"); //呼叫管道中的下一個委託 await next.Invoke(); await context.Response.WriteAsync("結束第一個委託 執行下一個委託之後\r\n"); }); app.Run(async context => { await context.Response.WriteAsync("進入第二個委託\r\n"); await context.Response.WriteAsync("Hello from 2nd delegate.\r\n"); await context.Response.WriteAsync("結束第二個委託\r\n"); }); } }
使用瀏覽器訪問有如下結果:
可以看出請求委託的執行順序是遵循上面的流程圖的。
注意:
響應傳送到客戶端後,請勿呼叫next.Invoke
。 響應開始之後,對HttpResponse的更改將丟擲異常。 例如,設定響應頭,狀態程式碼等更改將會引發異常。在呼叫next
之後寫入響應體。
- 可能導致協議違規。 例如,寫入超過
content-length
所述內容長度。 - 可能會破壞響應內容格式。 例如,將HTML頁尾寫入檔案。
HttpResponse.HasStarted是一個有用的提示,指示是否已傳送響應頭和/或正文已寫入。
順序
在Startup。Configure
方法中新增中介軟體元件的順序定義了在請求上呼叫它們的順序,以及響應的相反順序。 此排序對於安全性,效能和功能至關重要。
Startup.Configure
方法(如下所示)添加了以下中介軟體元件:
- 異常/錯誤處理
- 靜態檔案服務
- 身份認證
- MVC
public void Configure(IApplicationBuilder app) { app.UseExceptionHandler("/Home/Error"); // Call first to catch exceptions // thrown in the following middleware. app.UseStaticFiles(); // Return static files and end pipeline. app.UseAuthentication(); // Authenticate before you access // secure resources. app.UseMvcWithDefaultRoute(); // Add MVC to the request pipeline. }
上面的程式碼,UseExceptionHandler
是新增到管道中的第一個中介軟體元件,因此它捕獲以後呼叫中發生的任何異常。
靜態檔案中介軟體在管道中提前呼叫,因此可以處理請求和短路,而無需通過剩餘的元件。 靜態檔案中介軟體不提供授權檢查。 由其提供的任何檔案,包括wwwroot下的檔案都是公開的。
如果請求沒有被靜態檔案中介軟體處理,它將被傳遞給執行身份驗證的Identity中介軟體(app.UseAuthentication)。 身份不會使未經身份驗證的請求發生短路。 雖然身份認證請求,但授權(和拒絕)僅在MVC選擇特定的Razor頁面或控制器和操作之後才會發生。
授權(和拒絕)僅在MVC選擇特定的Razor頁面或Controller和Action之後才會發生。
以下示例演示了中介軟體順序,其中靜態檔案的請求在響應壓縮中介軟體之前由靜態檔案中介軟體處理。 靜態檔案不會按照中介軟體的順序進行壓縮。 來自UseMvcWithDefaultRoute的MVC響應可以被壓縮。
public void Configure(IApplicationBuilder app) { app.UseStaticFiles(); // Static files not compressed app.UseResponseCompression(); app.UseMvcWithDefaultRoute(); }
Use,Run,和 Map
你可以使用Use
,Run
和Map
配置HTTP管道。Use
方法可以使管道短路(即,可以不呼叫下一個請求委託)。Run
方法是一個約定, 並且一些中介軟體元件可能暴露在管道末端執行的Run [Middleware]方法。Map*
擴充套件用作分支管道的約定。對映根據給定的請求路徑的匹配來分支請求流水線,如果請求路徑以給定路徑開始,則執行分支。
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 WoSOYVVFafrom non-Map delegate. <p>"); }); } }
下表顯示了使用以前程式碼的 http://localhost:19219 的請求和響應:
請求 | 響應 |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
當使用Map時,匹配的路徑段將從HttpRequest.Path
中刪除,併為每個請求追加到Http Request.PathBase
。
MapWhen
根據給定謂詞的結果分支請求流水線。 任何型別為Func<HttpContext,bool>
的謂詞都可用於將請求對映到管道的新分支。 在以下示例中,謂詞用於檢測查詢字串變數分支的存在:
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>"); }); } }
以下下表顯示了使用上面程式碼 http://localhost:19219 的請求和響應:
請求 | 響應 |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/?branch=1 | Branch used = master |
Map
支援巢狀,例如:
app.Map("/level1",level1App => { level1App.Map("/level2a",level2AApp => { // "/level1/level2a" //... }); level1App.Map("/level2b",level2BApp => { // "/level1/level2b" //... }); });
Map
也可以一次匹配多個片段,例如:
app.Map("/level1/level2",HandleMultiSeg);
內建中介軟體
ASP.NET Core附帶以下中介軟體元件:
中介軟體 | 描述 |
---|---|
Authentication | 提供身份驗證支援 |
CORS | 配置跨域資源共享 |
Response Caching | 提供快取響應支援 |
Response Compression | 提供響應壓縮支援 |
Routing | 定義和約束請求路由 |
Session | 提供使用者會話管理 |
Static Files | 為靜態檔案和目錄瀏覽提供服務提供支援 |
URL Rewriting Middleware | 用於重寫 Url,並將請求重定向的支援 |
編寫中介軟體
中介軟體通常封裝在一個類中,並使用擴充套件方法進行暴露。 檢視以下中介軟體,它從查詢字串設定當前請求的Culture:
public class Startup
{
public void Configure(IApplicationBuilder app)
www.cppcns.com{
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
www.cppcns.com return next();
});
app.Run(async (context) =>
{
await context.Response.WriteAsync(
$"Hello {CultureInfo.CurrentCulture.DisplayName}");
});
}
}
您可以通過傳遞Culture來測試中介軟體,例如 http://localhost:19219/?culture=zh-CN
以下程式碼將中介軟體委託移動到一個類:
using Microsoft.AspNetCore.Http; using System.Globalization; using System.Threading.Tasks; namespace Culture { public class RequestCultureMiddleware { private readonly RequestDelegate _next; public RequestCultureMiddleware(RequestDelegate next) { _next = next; } public Task Invoke(HttpContext context) { var cultureQuery = context.Request.Query["culture"]; if (!string.IsNullOrWhiteSpace(cultureQuery)) { var culture = new CultureInfo(cultureQuery);http://www.cppcns.com CultureInfo.CurrentCulture = culture; CultureInfo.CurrentUICulture = culture; } // Call the next delegate/middleware in the pipeline return this._next(context); } } }
以下通過IApplicationBuilder的擴充套件方法暴露中介軟體:
using Microsoft.AspNetCore.Builder; namespace Culture { public static class RequestCultureMiddlewareExtensions { public static IApplicationBuilder UseRequestCulture( this IApplicationBuilder builder) { return builder.UseMiddleware<RequestCultureMiddleware>(); } } }
以下程式碼從Configure
呼叫中介軟體:
public class Startup { public void Configure(IApplicationBuilder app) { app.UseRequestCulture(); app.Run(async (context) => { await context.Response.WriteAsync( $"Hello {CultureInfo.CurrentCulture.DisplayName}"); }); } }
中介軟體應該遵循顯式依賴原則,通過在其建構函式中暴露其依賴關係。 中介軟體在應用程式生命週期構建一次。 如果您需要在請求中與中介軟體共享服務,請參閱以下請求相關性。
中介軟體元件可以通過構造方法引數來解析依賴注入的依賴關係。 UseMiddleware也可以直接接受其他引數。
每個請求的依賴關係
因為中介軟體是在應用程式啟動時構建的,而不是每個請求,所以在每個請求期間,中介軟體建構函式使用的作用域生命週期服務不會與其他依賴注入型別共享。 如果您必須在中介軟體和其他型別之間共享作用域服務,請將這些服務新增到Invoke方法的簽名中。 Invoke方法可以接受由依賴注入填充的其他引數。 例如:
public class MyMiddleware { private readonly RequestDelegate _next; public MyMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext,IMyScopedService svc) { svc.MyProperty = 1000; await _next(httpContext); } }
到此這篇關於ASP.NET Core中介軟體Middleware詳解的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援我們。