定制.NET 6.0的Middleware中間件

大家好,我是張飛洪,感謝您的閱讀,我會不定期和你分享學習心得,希望我的文章能成為你成長路上的墊腳石,讓我們一起精進。

在本文中,我們將學習中間件,以及如何使用它進一步定制應用程序。我們將快速學習中間件的基礎知識,然后探討如何使用它做的一些特殊事情。
本文涵蓋的主題包括:

  • 中間件簡介

  • 編寫自定義中間件

  • 中間件的潛力

  • 如何使用中間件

本章所處的位置,如下圖所示:
6aaaa94758d504db5b488b3fb14503cb.png

技術準備

我們使用控制臺、shell或Bash終端先創建一個ASP.NET Core MVC應用程序,然后切換到工作目錄:

dotnet new web -n MiddlewaresDemo -o MiddlewaresDemo

然后用VS打開項目:

cd MiddlewaresDemo code .

注意在.NET 6.0中,web項目模板發生了變化。Microsoft引入了minimal API,項目模板默認使用minimal API。

中間件簡介

大多數人可能已經知道中間件是什么,但有些人可能不知道,即使你已經在使用ASP.NET Core有一段時間了。我們一般不需要詳細了解中間件實例,因為它們大多隱藏在擴展方法后面,例如UseMvc()、UseAuthentication()、UseDeveloperExceptionPage()等。每次在Configure方法中,我們默認將隱式地使用至少一個或更多個中間件組件。

中間件組件是處理請求管道的一段代碼。我們可以將請求流程想象成一串管道,每次請求調用,都會返回一個響應。中間件負責創建回聲——它操縱請求上下文,加工處理、疊加邏輯、豐富信息。
6b00b2dc4d1f186766f93e82f10030e3.png

中間件組件按配置順序執行。配置的第一個中間件組件是第一個執行的組件。我們可以把中間件看成回旋鏢,出去的時候第一個執行,回來的時候最后一個執行。

在ASP.NET Core web應用程序,如果客戶端請求的是圖像或任何其他靜態文件,StaticFileMiddleware將負責查找該資源,如果找到該資源,則返回該資源。如果沒有,這個中間件除了調用下一個之外什么都不做。

MvcMiddleware組件檢查請求的資源,將其映射到已配置的路由,執行控制器,創建視圖,并返回HTML或Web API結果。如果MvcMiddleware沒有找到匹配的控制器,它無論如何都會返回一個結果——通常是一個404狀態的結果,這就是為什么MvcMiddleware是最后配置的中間件。

異常處理中間件通常是配置的第一批的中間件之一,不是因為它是第一個執行的,而是因為它是最后一個執行的。異常處理驗證結果,并以客戶端友好的方式在瀏覽器中顯示可能的異常。以下過程描述了運行時發生的500錯誤狀態:

var builder = WebApplication.CreateBuilder(args); 
var app = builder.Build(); 
app.MapGet("/", () => "Hello World!"); 
app.Run();

在ASP.NET Core 6.0,Microsoft引入了minimal API,它簡化了應用配置,并隱藏了許多默認配置,比如隱式的using聲明,因此,在頭部我們看不到任何using語句,以上就是我們看到的ASP.NET Core 6.0中的Program.cs?文件內容。
在這里,lambda中間件綁定到默認路由,只有一句簡單的“Hello World!”響應流。這個特殊的中間件會終止管道并返回響應內容。因此,它是最后一個運行的中間件。

下面我們把app.MapGet()做個替換,如下所示:

app.Use(async (context, next) =>{     await context.Response.WriteAsync("===");     await next();     await context.Response.WriteAsync("==="); 
}); 
app.Use(async (context, next) => { await context.Response.WriteAsync(">>>>>> ");     await next();     await context.Response.WriteAsync(" <<<<<<");
}); 
app.Run(async context => { await context.Response.WriteAsync("Hello World!"); 
});

這里調用兩個app.Use()方法,并且創建了兩個lambda中間件,除了做簡單的處理外,中間件還調用了它們的后繼組件,每個中間件的調用鏈很明確很清晰。在調用下一個中間件之前,處理實際的請求,在調用下個中間件之后,處理響應。以上就是管道的工作機制。
如果現在運行程序(使用dotnet run)并在瀏覽器中打開URL,我們應該會看到這樣的純文本結果

===>>>>>> Hello World! <<<<<<===

不知道您理解了沒?如果理解了,我們往下學習,看看如何使用這個概念向請求管道添加一些附加功能。

編寫自定義中間件

中間件可以說是ASP.NET Core的基座,在請求期間執行的所有邏輯都基于此機制。因此,我們可以使用它向web添加自定義功能。在下面案例,我們希望找出通過請求管道的每個請求的執行時間:

我們可以在調用下一個中間件之前創建并啟動秒表,然后在調用下個中間件之后停止測量執行時間,如下所示:

app.Use(async (context, next) => {     var s = new Stopwatch();     s.Start();     //其他操作 await next();     s.Stop(); //結束度量     var result = s.ElapsedMilliseconds;     //統計耗時     await context.Response.WriteAsync($"耗時:{result} 秒。"); 
});

記得為System.Diagnostics添加using語句。
之后,我們將經過的毫秒返回到響應流。
如果您編寫的中間件組件很多,Program.cs將變得非常混亂。所以大多數中間件組件將被編寫為獨立的類,如下所示:

using System.Diagnostics; 
public class StopwatchMiddleware { ???private readonly RequestDelegate _next; ????public StopwatchMiddleware(RequestDelegate next) ?{ ?_next = next; ?} ????public async Task Invoke(HttpContext context) { ????????var s = new Stopwatch(); ????????s.Start(); ????????//其他操作  ????????await _next(context); ????????s.Stop(); //結束度量 ????????var result = s.ElapsedMilliseconds; ????????//統計耗時     await context.Response.WriteAsync($"耗時:{result} 秒。");  ??}  
}

在Invoke方法中的,我們獲得構造函數和當前上下文獲得要執行的下一個中間件組件。

注意:
中間件在應用程序啟動時初始化,構造函數在應用程序生命周期內僅運行一次。另一方面,每個請求調用一次Invoke方法。
要使用此中間件,您可以使用一個通用的UseMiddleware方法:

app.UseMiddleware<StopwatchMiddleware>();

然而,更優雅的方法是創建一個封裝此調用的擴展方法:

public static class StopwatchMiddlewareExtension {     public static IApplicationBuilder  UseStopwatch(this IApplicationBuilder app)     {         app.UseMiddleware<StopwatchMiddleware>();         return app;    }}

然后就可以這樣使用:

app.UseStopwatch();

這樣,您可以通過請求管道向ASP.NET Core應用程序提供其他功能。中間件中提供了整個HttpContext。這樣,您可以使用中間件操縱請求和響應。

例如,AuthenticationMiddleware嘗試從請求中收集用戶信息。如果找不到任何信息,它將通過向客戶端發送特定的響應來請求信息。如果它找到,它會將其添加到請求上下文中,并以這種方式將其提供給整個應用程序。

中間件的潛力

使用中間件還可以做許多其他事情。例如,可以將請求管道拆分為兩個或多個管道,我們將在這里討論如何做到這一點。

使用/map分支管道

下一段代碼顯示了如何基于特定路徑創建請求管道的分支:

app.Map("/map1", app1 => {     // 其他中間件     app1.Run(async context =>     {         await context.Response.WriteAsync("Map Test 1");     }); 
}); 
app.Map("/map2", app2 => {     // 其他中間件     app2.Run(async context => {         await context.Response.WriteAsync("Map Test 2");     }); 
}); 
// 其他中間件

/map1路徑是一個特定的分支,它在內部繼續請求管道,/map2與此相同。這兩個map都有自己內部的中間件配置。所有其他未指定的路徑都遵循該主分支。

使用MapWhen分支管道

還有一個MapWhen方法可以根據條件分支管道,而不是根據路徑分支:

public void Configure(IApplicationBuilder app) {     app.MapWhen(context =>context.Request.Query.ContainsKey("分支"),         app1 => {            // 其他中間件           app1.Run(async context =>  {  await context.Response.WriteAsync( "MapBranch Test"); });     });     //其他中間件     app.Run(async context =>    { await context.Response.WriteAsync("Hello non-Map.");     });
}

使用中間件構造條件

我們一般可以根據配置值創建條件,或者根據請求上下文的屬性創建條件。在前面的示例中,我們使用了查詢字符串屬性作為條件。當然,你也可以使用HTTP標頭、表單屬性或請求上下文的任何其他屬性。

如果需要,還可以嵌套map以創建子分支和孫分支。
我們再看下健康檢查中間件,ASP.NET Core HealthCheck API的工作原理如下:
首先,它使用MapWhen指定要使用的端口,然后,它使用Map設置HealthCheck API路徑(如果未指定端口則使用Map)。最后,使用了HealthCheckMiddleware。我們看下面的代碼示例:

private static void UseHealthChecksCore(IApplicationBuilder app, PathString path, int? port, object[] args) 
{     if (port == null)    {         app.Map(path, b => b.UseMiddleware<HealthCheckMiddleware>(args)); }     else  {        app.MapWhen(c => c.Connection.LocalPort == port,b0 => b0.Map(path, b1 =>b1.UseMiddleware<HealthCheckMiddleware>(args)));     }; 
}

這里,我們可以使用Map或MapWhen分別基于特定路徑或特定條件提供特殊的API或資源。
接下來,讓我們看看如何在更新版本的ASP.NET Core中使用終止中間件組件。

在ASP.NET Core 3.0及更高版本中使用中間件

ASP.NET Core 3.0及更高版本,有兩種新的中間件,它們被稱為UseRoutingUseEndpoints

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {     if (env.IsDevelopment())     {  app.UseDeveloperExceptionPage();     }     app.UseRouting();     app.UseEndpoints(endpoints =>  {         endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello  World!");         });     }); 
}

第一個是使用路由的中間件UseRouting,另一個是訪問地址的UseEndpoints

這是新的端點路由。以前,路由是MVC的一部分,它只適用于MVC、Web API和基于MVC的框架。然而在ASP.NET Core 3.0及更高版本,路由不再是MVC框架中的一部分。現在,MVC和其他框架都可以被映射到特定的路由或端點。
在前面的代碼段中,GET請求被映射到頁面根URL。在下一個代碼片段中,MVC被映射到路由模式,RazorPages被映射到基于RazorPage的特定文件結構的路由:

app.UseEndpoints(endpoints => {     endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapRazorPages(); 
});

現在已經沒有UseMvc方法了,即使它仍然存在并在IApplicationBuilder對象級別上工作,以防止現有代碼中斷。現在,激活ASP.NET Core功能的方法更為精細。

  • Areas for MVC and web API:?endpoints.MapAreaControllerRoute(...);

  • MVC and web API:?endpoints.MapControllerRoute(...);

  • Blazor server-side:?endpoints.MapBlazorHub(...);

  • SignalR:?endpoints.MapHub(...);

  • Razor Pages:?endpoints.MapRazorPages(...);

  • Health checks:?endpoints.MapHealthChecks(...);

這些是ASP最常用的新Map方法。
還有很多方法可以定義回退地址,比如將路由和HTTP方法映射到代理,以及中間件組件。
你可以創建適用于所有請求的中間件,例如StopWatchMiddleware,你也可以編寫中間件以在特定路徑或路由上工作,例如創建一個Map方法,以將其映射到該路由。

注意事項
不再建議在中間件內部處理路由。相反,您應該使用新的地址路由。使用這種方法,中間件更加通用,它可以通過單一的配置就可以在多個路由上工作。

重寫終止中間件

接下來,我們創建小型虛擬中間件,將應用程序狀態寫入特定路由。在此示例中,沒有自定義路由處理:

namespace MiddlewaresSample; 
public class AppStatusMiddleware {     private readonly RequestDelegate _next;     private readonly string _status;public AppStatusMiddleware(RequestDelegate next, string status)     {        _next = next;         _status = status;    }     public async Task Invoke(HttpContext context)  {         await context.Response.WriteAsync($"Hello {_status}!");     } 
}

我們需要做的是在IEndpointRouteBuilder對象上編寫一個擴展方法。此方法將路由模式作為可選參數,并返回IEndpointConventionBuilder對象以啟用跨域資源共享(CORS)、身份驗證或路由的其他條件。

現在,我們應該添加一個擴展方法,以便更容易地使用中間件:

public static class MapAppStatusMiddlewareExtension {     public static IEndpointConventionBuilder MapAppStatus(this IEndpointRouteBuilder routes, string pattern = "/", string name = "World") {         var pipeline = routes.CreateApplicationBuilder().UseMiddleware<AppStatusMiddleware>(name).Build();         return routes.Map(pattern, pipeline).WithDisplayName("AppStatusMiddleware");     } 
}

完成后,我們可以使用MapAppStatus方法將其映射到特定路線:

app.UseRouting(); 
app.UseEndpoints(endpoints => {     endpoints.MapGet("/", () => "Hello World!");     endpoints.MapAppStatus("/status", "Status"); 
});

現在,我們可以通過輸入以下地址在瀏覽器中調用路由:?http://localhost:5000/status

總結

大多數ASP.NET Core功能基于中間件,在本章中,我們學習了中間件的工作原理以及如何創建自己的中間件組件來擴展ASP.NET框架。我們還學習了如何使用新路由向自定義的終止中間件添加路由。

在下一章中,我們將了解ASP.NET Core中的新端點路由,它允許我們以簡單靈活的方式創建自己的托管端點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280008.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280008.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280008.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python-循環控制--個人課堂筆記

Python中的兩種循環方式&#xff08;目前學到&#xff09;&#xff1a;for循環和while循環 for循環和while循環的區別&#xff1a; for循環一般用于控制循環的次數&#xff0c;while循環則是條件循環。 操作實例-猜數字小游戲&#xff08;3次猜錯提示游戲結束&#xff09;&…

刪除microsoft_如何從您的Microsoft帳戶中刪除設備

刪除microsoftWhen you sign into Windows 8 or 10 using your Microsoft account (and other Microsoft devices, like an Xbox), those devices become associated with your account. If you want to remove an old device you’ve gotten rid of, you’ll have to pay a vi…

線程的語法 (event,重要)

Python threading模塊 2種調用方式 直接調用 12345678910111213141516171819import threadingimport timedef sayhi(num): #定義每個線程要運行的函數print("running on number:%s" %num)time.sleep(3)if __name__ __main__:t1 threading.Thread(targetsayhi,args(…

求最大值和下標值

本題要求編寫程序&#xff0c;找出給定的n個數中的最大值及其對應的最小下標&#xff08;下標從0開始&#xff09;。 輸入格式: 輸入在第一行中給出一個正整數n&#xff08;1<n≤10&#xff09;。第二行輸入n個整數&#xff0c;用空格分開。 輸出格式: 在一行中輸出最大值及…

windows應用商店修復_如何修復Windows應用商店中的卡死下載

windows應用商店修復Though it’s had its share of flaky behavior since being introduced in Windows 8, the Windows Store has gotten more reliable over time. It still has the occasional problems, though. One of the more irritating issues is when an app update…

OpenWrt:Linux下生成banner

Linux下有三個小工具可以生成banner&#xff1a;1、banner使用#生成banner&#xff1b;2、figlet使用一些普通字符生成banner&#xff1b;3、toilet使用一些復雜的彩色特殊字符生成banner。使用apt-get安裝的時候需要輸入以下命令&#xff1a; $ sudo apt-get install sysvbann…

新冠病毒中招 | 第二天

今天跟大家分享我個人感染奧密克戎毒株第二天的經歷和感受。早上7點多自然醒來&#xff0c;已經沒有四肢乏力的感覺&#xff0c;但是身體的本能還是告訴我不愿意動彈。由于第一天躺著睡了一天&#xff0c;確實是躺得腰酸背疼的。起床量了一下體溫36.4正常&#xff0c;決定今天不…

輸出到Excel

HSSFWorkbook oBook new HSSFWorkbook(); NPOI.SS.UserModel.ISheet oSheet oBook.CreateSheet(); #region 輸出到Excel MemoryStream ms new MemoryStream(); oBook.Write(ms);string sExportPath ""; using (SaveFileDialog saveFileDialog1 new SaveFileDial…

JavaScript 精粹 基礎 進階(5)數組

轉載請注明出處 原文連接 blog.huanghanlian.com/article/5b6… 數組是值的有序集合。每個值叫做元素&#xff0c;每個元素在數組中都有數字位置編號&#xff0c;也就是索引。JS中的數組是弱類型的&#xff0c;數組中可以含有不同類型的元素。數組元素甚至可以是對象或其它數組…

icloud 購買存儲空間_如何釋放iCloud存儲空間

icloud 購買存儲空間Apple offers 5 GB of free iCloud space to everyone, but you’ll run up against that storage limit sooner than you’d think. Device backups, photos, documents, iCloud email, and other bits of data all share that space. Apple為每個人提供5 …

基于LAMP實現web日志管理查看

前言&#xff1a;日志是一個重要的信息庫&#xff0c;如何高效便捷的查看系統中的日志信息&#xff0c;是系統管理員管理系統的必備的技術。實現方式&#xff1a;1、將日志存儲于數據庫。2、采用LAMP架構&#xff0c;搭建PHP應用&#xff0c;通過web服務訪問數據庫&#xff0c;…

WPF效果第二百零七篇之EditableSlider

前面簡單玩耍一下快速黑白灰效果; 今天又玩了一下ZoomBlurEffect,來看看最終實現的效果:1、ps和cs文件都在Shazzam中,咱們自己隨意玩耍;今天主角是下面這位:2、來看看自定義控件布局(TextBox、Slider、ToggleButton)&#xff1a;3、點擊編輯按鈕,我就直接偷懶了:private void E…

閑話高并發的那些神話,看京東架構師如何把它拉下神壇

轉載:閑話高并發的那些神話&#xff0c;看京東架構師如何把它拉下神壇 高并發也算是這幾年的熱門詞匯了&#xff0c;尤其在互聯網圈&#xff0c;開口不聊個高并發問題&#xff0c;都不好意思出門。高并發有那么邪乎嗎&#xff1f;動不動就千萬并發、億級流量&#xff0c;聽上去…

c# Clone方法

clone是深拷貝&#xff0c;copy是淺拷貝&#xff0c;如果是值類型的話是沒什么區別的&#xff0c;如果是引用類型的話深拷貝拷貝的事整個對象的數據&#xff0c;而淺拷貝僅僅拷貝對象的引用。因為類的實例是引用類型&#xff0c;要想用原有的類中的實例的數據的話&#xff0c;既…

使用MyQ打開車庫門時如何接收警報

Chamberlain’s MyQ technology is great for opening and closing your garage door remotely with your smartphone, but you can also receive alerts whenever your garage door opens and closes (as well as receive alerts when it’s been open for an extended amount…

踏實工作,實現價值

工作&#xff0c;為實現自我價值 若想在漫長的職場生涯中穩步高升&#xff0c;首先要踏踏實實&#xff0c;專心致志、充滿激情的去完成工作中的每一項任務&#xff0c;無論工作是繁重的還是瑣碎的&#xff0c;都要嚴格要求自己全身心的去完成。而不是一味的抱怨&#xff0c;一味…

mac 防火墻禁止程序聯網_如何允許應用程序通過Mac的防火墻進行通信

mac 防火墻禁止程序聯網If you use a Mac, chances are you might not even realize that OS X comes with a firewall. This firewall helps ensure unauthorized app and services can’t contact your computer, and prevents intruders from sniffing out your Mac on a ne…

WPF-22 基于MVVM員工管理-02

我們接著上一節&#xff0c;這節我們實現crud操作&#xff0c;我們在EmployeeViewMode類中新增如下成員&#xff0c;并在構造函數中初始化該成員code snippetpublic EmployeeViewMode() {employeeService new EmployeeService();BindData();Employee new Employee();AddComma…

linux 3

-- Linux -- 開心的一天 vi   所有的 unix like 系統都會內置 vi 文本編輯器 vim  較多使用的,可以主動的以字體顏色辨別語法的正確性&#xff0c;方便程序設計 vi/vim 的使用 -- 命令模式&#xff08;Command mode&#xff09; 輸入模式&#xff08;Insert mode&#x…

從零開始搭建一個簡單的ui自動化測試框架02(pytest+selenium+allure)

二、先搭一個架子 在我還是小白連py語法都不太熟悉的時候&#xff0c;經常在網上看關于自學ui自動化測試的博客&#xff0c;最熟悉的套路莫過于先給你介紹一下selenium的各個api&#xff0c;然后寫一套代碼去登陸微博或者百度什么的&#xff0c;但我今天不愿意這么寫&#xff0…