【ASP.NET Core】MVC過濾器:運行流程

来源:https://www.cnblogs.com/tcjiaan/archive/2023/11/26/17855411.html
-Advertisement-
Play Games

MVC 的過濾器(Filters)也翻譯為“篩選器”。但是老周更喜歡翻譯為“過濾器”,意思上更好理解。 既然都叫過濾器了,就是在MVC的操作方法調用前後進行特殊處理的類型。比如: a、此調用是否已授權? b、在模型綁定之前要不要修改數據源?(可能含有兒童不宜的數據) c、在調用MVC方法前要不要改一 ...


MVC 的過濾器(Filters)也翻譯為“篩選器”。但是老周更喜歡翻譯為“過濾器”,意思上更好理解。

既然都叫過濾器了,就是在MVC的操作方法調用前後進行特殊處理的類型。比如:

a、此調用是否已授權?

b、在模型綁定之前要不要修改數據源?(可能含有兒童不宜的數據)

c、在調用MVC方法前要不要改一改輸入參數?在MVC方法調用之後要不要處理一下結果(加點味精,進一步調味)

d、發生異常後怎麼處理?

過濾器可解決上面一堆提問。

在 ASP.NET Core 的 MVC 框架中,所有過濾器都實現共同介面 IFilterMetadata。該介面空空如也,未定義任何成員。說白了,它的用處是作為一種“記號”。你怎麼證明你就是過濾器,嗯,看看你實現了 IFilterMetadata 介面沒?實現了就認定是過濾器。所以,該介面純粹是個角色標簽。

咱們寫代碼一般不會實現 IFilterMetadata 介面,畢竟裡面什麼卵方法都沒有,怎麼規範類型?因此,過濾器專屬命名空間 Microsoft.AspNetCore.Mvc.Filters 下為我們公開了以下介面,方便開發者實現:

1、IAuthorizationFilter:授權過濾器,它的優先順序最高,總是最先運行。看看你有沒有許可權調用 MVC 方法,若沒許可權,就 See you La La。

2、IResourceFilter:資源過濾器。它在授權過濾成功後、模型綁定前運行。可以檢查一下用於綁定的數據,要不要改一下。

3、IActionFilter:操作方法過濾器,就是針對 MVC Action 的。在操作方法運行前後運行,可以用來修改輸入參數值。

4、IResultFilter:結果過濾器。當 MVC 操作方法運行成功後就會運行,可以用來修改運行結果。比如加點 HTTP 消息頭什麼的。

5、IExceptionFilter:當 MVC 操作方法運行過程中發生異常才會運行,無異常就不會運行。

…… 其實還有的,但這裡咱們先不提,免得大伙搞得頭暈。

 

過濾器不止一個,同一類型的過濾還可能有多個,因此,它們就像中間件那樣,一個個鏈接起來,形成下水溝,哦不,是調用管道,或叫調用棧。於是,這就出現誰先運行的問題,雖然上面的介紹有說明,不過那太抽象了。任何編程知識只要能用代碼來驗證和觀察,就不用圖表和理論。

下麵,咱們實現上述幾個介面,然後往控制臺上列印一些文本,來看看這些過濾器是怎麼運行的。

public class CustAuthFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        Console.WriteLine("授權過濾器運行");
    }
}

public class CustResourceFilter : IResourceFilter
{
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        Console.WriteLine("資源過濾器 - " + $"{nameof(OnResourceExecuted)}方法運行");
    }

    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        Console.WriteLine("資源過濾器 - " + $"{nameof(OnResourceExecuting)}方法運行");
    }
}

public class CustActionFilter : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine("操作過濾器 - " + $"{nameof(OnActionExecuted)}方法運行");
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine("操作過濾器 - " + $"{nameof(OnActionExecuting)}方法運行");
    }
}

public class CustResultFilter : IResultFilter
{
    public void OnResultExecuted(ResultExecutedContext context)
    {
        Console.WriteLine("結果過濾器 - " + $"{nameof(OnResultExecuted)}方法運行");
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        Console.WriteLine("結果過濾器 - " + $"{nameof(OnResultExecuting)}方法運行");
    }
}

這裡我沒有實現異常過濾器,只實現了授權、資源、操作方法、結果這幾個有代表性的。

授權過濾器只要實現 OnAuthorization 方法即可。在實現代碼中,可以通過 HttpContext 對象查找授權有關的對象,如果確認是沒有訪問許可權的,可以設置一個自己定 Result 讓 MVC 操作方法的調用終止。

資源過濾器要實現兩個方法:OnResourceExecuting 方法在模型綁定前調用,這時你有機會修改數據源;OnResourceExecuted 方法是在資源過濾之後的其他過濾器運行結束才被調用,即:

ResourceExecuting
    ........ 剩餘過濾器.......
ResourceExecuted

Action 過濾器也要實現兩個方法:OnActionExecuting 在操作調用前運行;OnActionExecuted 是在操作方法調用後運行。

結果過濾器需要實現兩個方法:OnResultExecuting 方法在操作結果執行前調用,這裡可以修改 MVC 方法返回的值;OnResultExecuted 方法是在操作結果執行之後調用,一般這裡可以改改HTTP嚮應頭、Cookie 什麼的。

其實咱們剛實現的過濾器都是同步版本,這些過濾器都有配套的非同步版本,介面都是以 IAsync 開頭。這裡咱們先不用管同步非同步,避免搞得複雜了。也不要去理會過濾器是全局的還是局部的,下麵咱們統一把它們註冊為全局的。配置方法是通過 MVC 選項類的 Filters 集合,把要用的過濾器添加進去即可。

 var builder = WebApplication.CreateBuilder(args);
 builder.Services.AddControllersWithViews(options =>
 {
     // 配置全局過濾器
     options.Filters.Add<CustAuthFilter>();
     options.Filters.Add<CustResourceFilter>();
     options.Filters.Add<CustActionFilter>();
     options.Filters.Add<CustResultFilter>();
 });
 var app = builder.Build();

添加一個“狗頭”控制器,用於測試。

public class GouTouController : Controller
{
    public IActionResult Index()
    {
        Console.WriteLine("Index操作運行");
        return View();
    }
}

 

為了防止 ASP.NET Core 應用程式輸出的日誌干擾咱們查看控制台內容,咱們禁用所有日誌輸出。打開 appsettings.json 文件,把所有日誌類別的記錄級別改為 None。

{
  "Logging": {
    "LogLevel": {
      "*": "None"
    }
  },
  "AllowedHosts": "*"
}

星號 * 的意思就是代表所有類別的日誌,LogLevel 為 None 就不會輸出日誌了(貌似有個別日誌禁用不了)。

運行程式後,控制台列印出這樣的內容:

 這個流程現在是不是很清晰了?咱們畫圖表了,直接這樣表達就好:

Author
Resource Executing
    Action Executing
            Action Running
    Action Executed
    Result Executing
            Result Running
    Result Executed
Resource Executed

 

局部過濾器的運行過程與全局過濾器相同,如果局部和全局過濾器同時使用,那會發生什麼呢?咱們試試。

接下來我們為授權過濾、資源過濾、操作過濾、結果過濾各創建兩個類——用於局部和全局。實際開發中一般不需要這樣搞,通常全局和局部寫一個類就行,畢竟過濾器類型在全局和局部是通用的。我這裡只為了演示。局部過濾器是通過特性類的方式應用到 MVC 方法上的,所以,局部過濾器除了實現過濾器介面,還要從 Attribute 類派生。

1、實現局部、全局授權過濾器。

// 授權過濾器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyAuthorFilterAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        Console.WriteLine("局部:授權過濾器運行");
    }
}

// 授權過濾器-全局
public class GlobAuthorFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        Console.WriteLine("全局:授權過濾器運行");
    }
}

2、實現局部、全局資源過濾器。

// 資源過濾器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        Console.WriteLine("局部:資源過濾器-Executed");
    }

    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        Console.WriteLine("局部:資源過濾器-Executing");
    }
}

// 資源過濾器-全局
public class GlobResourceFilter : IResourceFilter
{
    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        Console.WriteLine("全局:資源過濾器-Executed");
    }

    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        Console.WriteLine("全局:資源過濾器-Executing");
    }
}

3、實現局部、全局操作過濾器。

// 操作過濾器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyActionFilterAttribute : Attribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine("局部:操作過濾器-Executed");
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine("局部:操作過濾器-Executing");
    }
}

// 操作過濾器-全局
public class GlobActionFilter : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine("全局:操作過濾器-Executed");
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine("全局:操作過濾器-Executing");
    }
}

4、實現局部、全局結果過濾器。

// 結果過濾器-局部
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class MyResultFilterAttribute : Attribute, IResultFilter
{
    public void OnResultExecuted(ResultExecutedContext context)
    {
        Console.WriteLine("局部:結果過濾器-Executed");
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        Console.WriteLine("局部:結果過濾器-Executing");
    }
}

// 結果過濾器-全局
public class GlobResultFilter : IResultFilter
{
    public void OnResultExecuted(ResultExecutedContext context)
    {
        Console.WriteLine("全局:結果過濾器-Executed");
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        Console.WriteLine("全局:結果過濾器-Executing");
    }
}

 

先註冊全局過濾器。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
    // 添加全局過濾器
    options.Filters.Add<GlobActionFilter>();
    options.Filters.Add<GlobAuthorFilter>();
    options.Filters.Add<GlobResourceFilter>();
    options.Filters.Add<GlobResultFilter>();
});
var app = builder.Build();

局部過濾器以特性方式應用於 MVC 操作方法。

public class SpiderController : ControllerBase
{
    [MyResourceFilter]
    [MyResultFilter]
    [MyActionFilter, MyAuthorFilter]
    public IActionResult Index()
    {
        Console.WriteLine("Index操作被調用");
        return Content("大火燒了毛毛蟲");
    }
}

和上一個例子一樣,禁用日誌輸出(appsettings.json文件)。

{
  "Logging": {
    "LogLevel": {
      "*": "None"
    }
  },
  ……
}

程式運行後,控制台列印以下內容:

 過濾器按 授權->資源->操作->結果 運行的次序不變。在同種過濾器中,全局過濾器優先運行。

全局授權過濾器
局部授權過濾器
全局資源過濾器 - 前
    局部資源過濾器 - 前
        全局操作過濾器 - 前
            局部操作過濾器 - 前
                【調用 MVC 操作方法】
            局部操作過濾器 - 後
        全局操作過濾器 - 後
        全局結果過濾器 - 前
             局部結果過濾器 - 前
                【執行操作結果】
             局部結果過濾器 - 後
        全局結果過濾器 - 後
    局部資源過濾器 - 後
全局資源過濾器 - 後

 

另外,有一件事要註意:如果你的控制器的基類是 Controller,那麼,還有優先更高的 Action Filter。看看 Controller 類它實現了啥介面。

public abstract class Controller : ControllerBase, IActionFilter, IFilterMetadata, IAsyncActionFilter, IDisposable

咱們把剛纔的控制器代碼改一下,讓它繼承 Controller 類,並重寫 OnActionExecuting、OnActionExecuted 方法。

public class SpiderController : Controller
{
    ……

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine("控制器實現的操作過濾器-Executing");
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine("控制器實現的操作過濾器-Executed");
        base.OnActionExecuted(context);
    }
}

然後再次運行程式,控制台將列印以下內容:

看,這個由控制器類實現的 Action 過濾器比全局的還早運行。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Jinja2,由Flask框架的創作者開發,是一款功能豐富的模板引擎,以其完整的Unicode支持、靈活性、高效性和安全性而備受推崇。最初受Django模板引擎啟發,Jinja2為Flask提供了強大的模板支持,後來也成為其他項目的首選。在本文中,我們將深入探討Jinja2的特性、語法以及如何在Fl... ...
  • 類應該是被封裝的,類的用戶通過介面使用類提供的功能,而不必關心類的內部如何實現。然而,C++標準庫容器 std::vector 的實現滲透到了介面中來。對於以下代碼: const int pushNum = 10; std::vector<int> v = { 1,2,3 }; int* p = & ...
  • Bug1 自定義被限流的後續操作@SentinelResource(blockHandler = "blockHandler"),其中blockHandler處理函數不執行 前置條件 //定義的資源 @GetMapping("/resource") @SentinelResource(value = ...
  • 電腦安全和數據隱私是現代應用程式設計中至關重要的方面。為了確保數據的機密性和完整性,常常需要使用加密和解密演算法。C++是一種廣泛使用的編程語言,提供了許多加密和解密演算法的實現。本文將介紹一些在C++中常用的加密與解密演算法,這其中包括Xor異或、BASE64、AES、MD5、SHA256、RSA等。 ...
  • 位運算 題目背景 題目由 daiyulong20120222 創作(me) 並由 QBW1117完善以及數據 。 題目描述 給定兩個數\(x,y\) ,在給定一個位運算符號 \(c\)。 請你列出 \(x,y\) 進行 \(c\) 位運算是的算數豎式式。 註: 豎式這麼列: 顯示出兩個數的完整二進位 ...
  • 主要介紹了第一個Spring MVC程式的環境搭建、父子項目結構、Tomcat配置、配置文件。對DispatcherServlet的講解,2個核心類型:1.RequestMappingHandlerMapping 2.RequestMappingHandlerAdapter,視圖解析器ViewRes... ...
  • matplotlib是基於python生態開發的一個可視化繪圖庫,它的出現讓python在數據分析及機器學習方面占了重要的一部分,目前很多數據分析及機器學習相關方面的工程都有使用到這個庫,並且由於其簡單易用,安裝簡單等方面的優勢深得廣大開發者的喜愛。 ...
  • HtmlAgilityPack是一個.NET平臺下的HTML解析庫,它可以將HTML文本轉換為DOM文檔對象,方便我們對HTML文本進行操作和分析。HtmlAgilityPack支持XPath語法,可以通過XPath表達式來獲取DOM節點,同時還提供了一些方便的API,可以實現HTML文本的解析、修 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...