中間件和Filter 有哪些區別,分別的作用又是什麼,使用Endpoint 終結者路由的應用場景有哪些,怎麼使用Endpoint 終結者路由進行中間件的開發;我們知道,任何的一個web框架都是把http請求封裝成一個管道,每一次的請求都是經過管道的一系列操作,最終到達我們寫的代碼中。那麼中間件就是在... ...
一、概述
這篇文章主要分享Endpoint
終結點路由的中間件
的應用場景及實踐案例,不講述其工作原理,如果需要瞭解工作原理的同學,
可以點擊查看以下兩篇解讀文章:
1.1 中間件(Middleware)的作用
我們知道,任何的一個web框架都是把http請求封裝成一個管道,每一次的請求都是經過管道的一系列操作,最終到達我們寫的代碼中。那麼中間件就是在應用程式管道中的一個組件,用來攔截請求過程進行一些其他處理和響應。中間件可以有很多個,每一個中間件都可以對管道中的請求進行攔截,它可以決定是否將請求轉移給下一個中間件。
asp.net core 提供了IApplicationBuilder
介面來讓把中間件註冊到asp.net的管道請求當中去,中間件是一個典型的AOP
應用。 下麵是一個微軟官方的一個中間件管道請求圖:
1.2 中間件和過濾器的區別
Filter
是延續ASP.NET MVC的產物,同樣保留了五種的Filter,分別是Authorization Filter、Resource Filter、Action Filter、Exception Filter及Result Filter。
具體可以查看我上次分享的一篇Asp.Net Core Filter 深入淺出的那些事-AOP 的文章.
根據描述,可以看出中間件和過濾器的功能類似,那麼他們有什麼區別?為什麼又要搞一個中間件呢?
其實,過濾器和中間件他們的關註點是不一樣的,也就是說職責不一樣,乾的事情就不一樣。
同作為兩個AOP
利器,Filter
(過濾器)更貼合業務,它關註於應用程式本身,比如你看ActionFilter
和 ResultFilter
,它都直接和你的Action
,ActionResult
交互了,是不是離你很近的感覺,那我有一些比如對我的輸出結果進行格式化,對我的請求的ViewModel進行數據驗證啦,肯定就是用Filter無疑了。它是MVC的一部分,它可以攔截到你Action上下文的一些信息,而中間件是沒有這個能力的。
可以看到,每一個中間件都都可以在請求之前和之後進行操作。請求處理完成之後傳遞給下一個請求
1.3 中間件的使用場景
那麼,何時使用中間件呢?我的理解是在我們的應用程式當中和業務關係不大的一些需要在管道中做的事情可以使用,比如身份驗證,Session存儲,日誌記錄等。其實我們的 Asp.net core項目中本身已經包含了很多個中間件。比如 身份認證中間件 UseAuthorization()
等系列.
二、中間件實戰
需求場景:通過後端記錄每一次的訪問請求日誌,同時需要根據需要排除一些Controller
或者Action
不記錄請求的日誌信息。
思考:經過分析我需要創建一個全局的中間件進行攔截路由,並且寫入日誌;同時需要添加一個特性Attribute
進行標註那些Controller
或者Action
不需要進行日誌記錄。
我們來創建LogsMiddleware
中間件代碼,代碼如下:
public class LogsMiddleware
{
private readonly RequestDelegate _next;
public LogsMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context)
{
var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;
if (endpoint == null)
{
await _next(context);
return;
}
using (var scope = context.RequestServices.CreateScope())
{
var _logger = scope.ServiceProvider.GetService<ILogger<LogsMiddleware>>();
var attruibutes = endpoint.Metadata.OfType<NoLogsAttriteFilter>();
if (attruibutes.Count()==0)
{
_logger.LogInformation($" url:{context.Request.Path}, 訪問時間:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
}
//記錄 排除的特殊Message 信息
foreach (var attribute in attruibutes)
{
_logger.LogInformation(attribute.Message);
}
}
await _next(context);
}
}
NoLogsAttriteFilter
過濾器代碼如下:
public class NoLogsAttriteFilter : Attribute
{
/// <summary>
/// 這裡加這個主要是把獲取到的信息在中間件中列印出來,區分中間件的攔截用處
/// </summary>
public string Message = "";
public NoLogsAttriteFilter(string message)
{
Message = message;
}
}
Startup
中的代碼如下:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseRouting();
app.UseAuthorization();
app.UseMiddleware<LogsMiddleware>();//添加日誌記錄中間件
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
HomeController
控制器中的兩個Action 代碼如下::
// 訪問該路由會記錄訪問日誌
public IActionResult Index()
{
return View();
}
//訪問該路由不會記錄訪問日誌
[NoLogsAttriteFilter("Manage 不需要記錄訪問日誌")]
public IActionResult Manage()
{
return View();
}
這樣就自定義日誌中間件就已經完成了我上面的需求,不依賴於任何業務獨立存在於系統中;從代碼中我們可以看到中間件通過context.Features.Get<IEndpointFeature>()?.Endpoint;
方法獲得終結點路由方式進行匹配,並且可以通過endpoint.Metadata.OfType<NoLogsAttriteFilter>()
方式獲得Action
中的特性信息數據,並通過該攔截進行我的需求
自定義中間件教程文章請點擊自定義中間件官方教程 一文。
現在我們再來印證下我上一篇關於 Asp.Net Core EndPoint 終結點路由工作原理解讀 一文 中提及到UseRouting()
中間件是遍歷所有的Endpoint
終結點路由以匹配當前請求的 Endpoint
終結點路由一說,我把註冊LogsMiddleware
中間件和UseRouting()
路由中間件代碼順序調整一下,代碼如下:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
// 中間件註冊放到了UseRouting() 之前
app.UseMiddleware<LogsMiddleware>();//添加日誌記錄中間件
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
再來看看運行調試的結果如圖:
從調試的結果圖中可以看出 endpoint
變數是 null;所有需要使用到Endpoint
終結點路由必須註冊在UseRouting()
中間件之後。
三、官方常用中間件
- 異常/錯誤處理
當應用在開發環境中運行時:
開發人員異常頁中間件 (UseDeveloperExceptionPage
) 報告應用運行時錯誤。
資料庫錯誤頁中間件報告資料庫運行時錯誤。
當應用在生產環境中運行時:
異常處理程式中間件 (UseExceptionHandler
) 捕獲以下中間件中引發的異常。
HTTP 嚴格傳輸安全協議 (HSTS) 中間件 (UseHsts) 添加 Strict-Transport-Security 標頭。 - HTTPS 重定向中間件 (
UseHttpsRedirection
) 將 HTTP 請求重定向到 HTTPS。 - 靜態文件中間件 (
UseStaticFiles
) 返回靜態文件,並簡化進一步請求處理。 - Cookie 策略中間件 (
UseCookiePolicy
) 使應用符合歐盟一般數據保護條例 (GDPR) 規定。 - 用於路由請求的路由中間件 (
UseRouting
)。 - 身份驗證中間件 (
UseAuthentication
) 嘗試對用戶進行身份驗證,然後才會允許用戶訪問安全資源。 - 用於授權用戶訪問安全資源的授權中間件 (
UseAuthorization
)。 - 會話中間件 (
UseSession
) 建立和維護會話狀態。 如果應用使用會話狀態,請在 Cookie 策略中間件之後和 MVC 中間件之前調用會話中間件。 - 用於將
Razor Pages
終結點添加到請求管道的終結點路由中間件(帶有 MapRazorPages 的UseEndpoints
)。
如果您覺的不錯,請微信掃碼關註 【dotNET博士】公眾號,後續給您帶來更精彩的分享
以上如果有錯誤的地方,請大家積極糾正,謝謝大家的支持!!