ASP.NET Core 中文文檔 第四章 MVC(4.3)過濾器

来源:http://www.cnblogs.com/dotNETCoreSG/archive/2016/10/07/aspnetcore-4_4_3-filters.html
-Advertisement-
Play Games

ASP.NET MVC 過濾器 可在執行管道的前後特定階段執行代碼。過濾器可以配置為全局有效、僅對控制器有效或是僅對 Action 有效。 ...


原文:Filters
作者:Steve Smith
翻譯:劉怡(AlexLEWIS)
校對:何鎮汐

ASP.NET MVC 過濾器 可在執行管道的前後特定階段執行代碼。過濾器可以配置為全局有效、僅對控制器有效或是僅對 Action 有效。

查看或下載演示代碼.

過濾器如何工作?

不同的過濾器類型會在執行管道的不同階段運行,因此它們各自有一套適用場景。根據你實際要解決的問題以及在請求管道中執行的位置來選擇創建不同的過濾器。運行於 MVC Action 調用管道內的過濾器有時被稱為 過濾管道 ,當 MVC 選擇要執行哪個 Action 後就會先執行該 Action 上的過濾器。

不同過濾器在管道的不同位置運行。像授權這樣的過濾器只運行在管道的靠前位置,並且其後也不會跟隨 action。其它過濾器(如 action 過濾器等)可以在管道的其它部分之前或之後執行,如下所示。

選擇過濾器

授權過濾器 用於確定當前用戶的請求是否合法。

資源過濾器 是授權之後第一個用來處理請求的過濾器,也是最後一個接觸到請求的過濾器(因為之後就會離開過濾器管道)。在性能方面,資源過濾器在實現緩存或短路過濾器管道尤其有用。

Action 過濾器 包裝了對單個 action 方法的調用,可以將參數傳遞給 action 並從中獲得 action result。

異常過濾器 為 MVC 應用程式未處理異常應用全局策略。

結果過濾器 包裝了單個 action result 的執行,當且僅當 action 方法成功執行完畢後方纔運行。它們是理想的圍繞視圖執行或格式處理的邏輯(所在之處)。

實現

所有過濾器均可通過不同的介面定義來支持同步和非同步實現。根據你所需執行的任務的不同來選擇同步還是非同步實現。從框架的角度來講它們是可以互換的。
同步過濾器定義了 OnStageExecuting 方法和 OnStageExecuted 方法(當然也有例外)。OnStageExecuting 方法在具體事件管道階段之前調用,而 OnStageExecuted 方法則在之後調用(比如當 Stage 是 Action 時,這兩個方法便是 OnActionExecuting 和 OnActionExecuted,譯者註)。

using FiltersSample.Helper;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class SampleActionFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // do something before the action executes
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // do something after the action executes
        }
    }
}

非同步過濾器定義了一個 On\ Stage\ ExecutionAsync 方法,可以在具體管道階段的前後運行。On\ Stage\ ExecutionAsync 方法被提供了一個 Stage\ ExecutionDelegate 委托,當調用時該委托會執行具體管道階段的工作,然後等待其完成。

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class SampleAsyncActionFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(
            ActionExecutingContext context,
            ActionExecutionDelegate next)
        {
            // do something before the action executes
            await next();
            // do something after the action executes
        }
    }
}

註解
只能 實現一個過濾器介面,要麼是同步版本的,要麼是非同步版本的,魚和熊掌不可兼得 。如果你需要在介面中執行非同步工作,那麼就去實現非同步介面。否則應該實現同步版本的介面。框架會首先檢查是不是實現了非同步介面,如果實現了非同步介面,那麼將調用它。不然則調用同步介面的方法。如果一個類中實現了兩個介面,那麼只有非同步方法會被調用。最後,不管 action 是同步的還是非同步的,過濾器的同步或是非同步是獨立於 action 的。

過濾器作用域

過濾器具有三種不同級別的 作用域 。你可以在特定的 action 上用特性(Attribute)的方式使用特定的過濾器;也可以在控制器上用特性的方式使用過濾器,這樣就會將效果應用在控制器內所有的 action 上;或者註冊一個全局過濾器,它將作用於整個 MVC 應用程式下的每一個 action。

如果想要使用全局過濾器的話,在你配置 MVC 的時候在 StartupConfigureServices 方法中添加:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(SampleActionFilter)); // by type
        options.Filters.Add(new SampleGlobalActionFilter()); // an instance
    });

    services.AddScoped<AddHeaderFilterWithDi>();
}

過濾器可通過類型添加,也可以通過實例添加。如果通過實例添加,則該實例會被用於每一個請求。如果通過類型添加,則將會 type-activated(意思是說每次請求都會創建一個實例,其所有構造函數依賴項都將通過 DI 來填充)。通過類型添加過濾器相當於 filters.Add(new TypeFilterAttribute(typeof(MyFilter)))

把過濾器介面的實現當做 特性(Attributes) 使用是極為方便的。過濾器特性(filter attributes)可應用於控制器(Controllers)和 Action 方法。框架包含了內置的基於特性的過濾器,你可繼承它們或另外定製。比方說,下例過濾器繼承了 ResultFilterAttribute ,並重寫(override)了 OnResultExecuting 方法(在響應中增加了一個頭信息)。

using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class AddHeaderAttribute : ResultFilterAttribute
    {
        private readonly string _name;
        private readonly string _value;

        public AddHeaderAttribute(string name, string value)
        {
            _name = name;
            _value = value;
        }

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                _name, new string[] { _value });
            base.OnResultExecuting(context);
        }
    }
}

特性允許過濾器接收參數,如下例所示。可將此特性加諸控制器(Controller)或 Action 方法,併為其指定所需 HTTP 頭的名稱和值,並將該 HTTP 頭加入響應中:

[AddHeader("Author", "Steve Smith @ardalis")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using developer tools.");
    }
}

Index Action 的結果如下所示:響應的頭信息顯示在右下角。

以下幾種過濾器介面可以自定義為相應特性的實現。

過濾器特性:

取消與短路

通過設置傳入過濾器方法的上下文參數中的 Result 屬性,可以在過濾器管道的任意一點短路管道。比方說,下麵的 ShortCircuitingResourceFilter 將阻止所有它之後管道內的所有過濾器,包括所有 action 過濾器。

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class ShortCircuitingResourceFilterAttribute : Attribute,
            IResourceFilter
    {
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            context.Result = new ContentResult()
            {
                Content = "Resource unavailable - header should not be set"
            };
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }
    }
}

在下例中,ShortCircuitingResourceFilterAddHeader 過濾器都指向了名為 SomeResource 的 action 方法。然而,由於首先運行的是 ShortCircuitingResourceFilter ,它短路了剩下的管道,SomeResource 上的 AddHeader 過濾器不會運行。如果這兩個過濾器都以 Action 方法級別出現,它們的結果也會是一樣的(只要 ShortCircuitingResourceFilter 首先運行,請查看 Ordering )。

[AddHeader("Author", "Steve Smith @ardalis")]
public class SampleController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header should be set.");
    }

配置過濾器

全局過濾器在 Startup.cs 中配置。基於特性的過濾器如果不需要任何依賴項的話,可以簡單地繼承一個與已存在的過濾器相對應的特性類型。如果要創建一個 全局作用域、但需要從依賴註入(DI)中獲得依賴項的過濾器,在它們上面加上 ServiceFilterAttributeTypeFilterAttribute 特性,這樣就可用於控制器或 action 了。

依賴註入

以特性形式實現的、直接添加到控制器(Controller)類或 Action 方法的過濾器,其構造函數不得由 依賴註入 (DI)提供依賴項。其原因在於特性所需的構造函數參數必須由使用處直接提供。這是特性原型機理的限制。

不過,如果過濾器需要從 DI 中獲得依賴項,那麼有幾種辦法可以實現,可在類(class)或 Action 方法使用:

TypeFilter 將為其依賴項從 DI 中使用服務(services)來實例化一個實例。ServiceFilter 則從 DI 中取回一個過濾器實例。下例中將演示如何使用 ServiceFilter

[ServiceFilter(typeof(AddHeaderFilterWithDi))]
public IActionResult Index()
{
    return View();
}

如果在 ConfigureServices 中直接使用未經註冊的 ServiceFilter 過濾器,則會拋出以下異常:

System.InvalidOperationException: No service for type
'FiltersSample.Filters.AddHeaderFilterWithDI' has been registered.

為避免此異常,你必須在 ConfigureServices 中為 AddHeaderFilterWithDI 類型註冊:

services.AddScoped<AddHeaderFilterWithDi>();

ServiceFilterAttribute 實現了 IFilterFactory 介面,後者暴露了創建 IFilter 實例的單一方法。在 ServiceFilterAttribute 中,介面 IFilterFactory 中定義的 CreateInstance 方法被實現為用於從服務容器(DI)載入指定類型。

TypeFilterAttribute 很像 ServiceFilterAttribute (它同樣是 IFilterFactory 的實現),但此類型並非直接解析自 DI 容器。
相反,它通過使用 Microsoft.Extensions.DependencyInjection.ObjectFactory 來實例化類型。

由於這種不同,使用 TypeFilterAttribute 引用的類型不需要在使用之前向容器註冊(但它們依舊將由容器來填充其依賴項)。同樣地,TypeFilterAttribute 能可選地接受該類型的構造函數參數。下例演示如何向使用 TypeFilterAttribute 修飾的類型傳遞參數:

[TypeFilter(typeof(AddHeaderAttribute),
    Arguments = new object[] { "Author", "Steve Smith (@ardalis)" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

若是你有一個簡單的不需要任何參數的、但其構造函數需要通過 DI 填充依賴項的過濾器的話,你可以通過繼承 TypeFilterAttribute,在類(class)或方法(method)上使用自己命名的特性(來取代 [TypeFilter(typeof(FilterType))])。下例過濾器向你展示這是如何實現的:

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute():base(typeof(SampleActionFilterImpl))
    {
    }

    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation("Business action starting...");
            // perform some business logic work

        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // perform some business logic work
            _logger.LogInformation("Business action completed.");
        }
    }
}

該過濾器可通過使用 [SampleActionFilter] 這樣的語法應用於類或方法,而不必使用 [TypeFilter][ServiceFilter]

註解
應避免純粹為記錄日誌而創建和使用過濾器,這是因為 內建的框架日誌功能 應該已經提供了你所需的功能。如果你要把日誌記錄功能放入過濾器中,它應專註於業務領域或過濾器的具體行為,而不是 MVC Action 或框架事件。

IFilterFactory 實現了 IFilter。因此,在過濾器管道的任何地方 IFilterFactory 實例都可當做 IFilter 實例來使用。當框架準備調用過濾器,將試圖把其強制轉換為 IFilterFactory。如果轉換成功,將通過調用 CreateInstance 方法創建即將被調用的 IFilter 實例。因為過濾器管道不需要在應用程式啟動時顯式設置了,所以這是一種非常靈活的設計。

你可以在自己的特性實現中實現 IFilterFactory 介面,以此來實現另一種創建過濾器的方法:

public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new InternalAddHeaderFilter();
    }

    private class InternalAddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "Internal", new string[] { "Header Added" });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

排序

過濾器可應用於 Action 方法、控制器(Controller,通過特性(attribute)的形式)或添加到全局過濾器集合中。其作用域通常還決定了其執行順序。最靠近 Action 的過濾器首先執行;通常來講通過重寫行為而不是顯式設置順序來改變順序。這有時被稱為“俄羅斯套娃”,因為每一個作用範圍都包裹了前一個作用範圍,就像是 套娃 那般。

除了作用範圍,過濾器還可以通過實現 IOrderedFilter 來重寫它們的執行順序。該介面只是簡單地暴露了 int Order 屬性,然後執行時根據該數字正排序(數字越小,越先執行)後依次執行過濾器。所有內建過濾器(包括 TypeFilterAttributeServiceFilterAttribute)都實現了 IOrderedFilter 介面,因此當你將過濾器以特性的方式用於類(class)或方法(method)時你可以指定每一個過濾器的執行順序。預設情況下所有內建過濾器的 Order 屬性值都為 0,因此作用範圍就當做決定性的因素(除非存在不為 0 的 Order 值)。

每個繼承自 Controller 基類的控制器(Controller)都包含 OnActionExecutingOnActionExecuted 方法。這些方法為給定的 Action 包裝了過濾器,它們分別在最先和最後運行。基於作用範圍的順序(假設沒有為過濾器的 Order 設置任何值):

  1. The Controller OnActionExecuting
  2. The Global filter OnActionExecuting
  3. The Class filter OnActionExecuting
  4. The Method filter OnActionExecuting
  5. The Method filter OnActionExecuted
  6. The Class filter OnActionExecuted
  7. The Global filter OnActionExecuted
  8. The Controller OnActionExecuted

註解
當過濾器將啟動運行、需要決定過濾器執行順序時,IOrderedFilter 會向外宣佈自己的作用範圍。過濾器首先通過 order 來排序,然後通過作用範圍來決定。如果不設置,則 Order 預設為 0。

為在基於作用範圍的排序中修改預設值,你須在類一級(class-level)或方法一級(method-level)的過濾器上顯式設置 Order 屬性。比如為方法一級的特性增加 Order=-1

[MyFilter(Name = "Method Level Attribute", Order=-1)]

這種情況下,小於 0 的值將確保該過濾器在全局過濾器和類一級過濾器之前運行(假設它們的 Order 屬性均未設置)。

新的排序可能是這樣的:

  1. The Controller OnActionExecuting
  2. The Method filter OnActionExecuting
  3. The Global filter OnActionExecuting
  4. The Class filter OnActionExecuting
  5. The Class filter OnActionExecuted
  6. The Global filter OnActionExecuted
  7. The Method filter OnActionExecuted
  8. The Controller OnActionExecuted

註解
Controller 類的方法總是在所有過濾器之前和之後運行。這些方法並未實現為 IFilter 實現,同時它們不參與 IFilter 的排序演算法。

授權過濾器

授權過濾器 控制對 action 方法的訪問,也是過濾器管道中第一個被執行的過濾器。它們只有一個前置階段,不像其它大多數過濾器支持前置階段方法和後置階段方法。只有當你使用自己的授權框架時才需要定製授權過濾器。謹記勿在授權過濾器內拋出異常,這是因為所拋出的異常不會被處理(異常過濾器也不會處理它們)。此時記錄該問題或尋求其它辦法。

更多請訪問 Authorization

資源過濾器

資源過濾器 要麼實現 IResourceFilter 介面,要麼實現 IAsyncResourceFilter 介面,它們執行於大多數過濾器管道(只有 授權過濾器 在其之前運行,其餘所有過濾器以及 Action 處理均出現在其 OnResourceExecutingOnResourceExecuted 方法之間)。當你需要短路絕大多數正在進行的請求時,資源過濾器特別有用。資源過濾器的一個典型例子是緩存,如果響應已經被緩存,過濾器會立即將之置為結果以避免後續 Action 的多餘操作過程。

上面所說的是一個 短路資源過濾器 的例子。下例是一個非常簡單的緩存實現(請勿將之用於生產環境),只能與 ContentResult 配合使用,如下所示:

public class NaiveCacheResourceFilterAttribute : Attribute,
    IResourceFilter
{
    private static readonly Dictionary<string, object> _cache
                = new Dictionary<string, object>();
    private string _cacheKey;

    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        _cacheKey = context.HttpContext.Request.Path.ToString();
        if (_cache.ContainsKey(_cacheKey))
        {
            var cachedValue = _cache[_cacheKey] as string;
            if (cachedValue != null)
            {
                context.Result = new ContentResult()
                { Content = cachedValue };
            }
        }
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        if (!String.IsNullOrEmpty(_cacheKey) &&
            !_cache.ContainsKey(_cacheKey))
        {
            var result = context.Result as ContentResult;
            if (result != null)
            {
                _cache.Add(_cacheKey, result.Content);
            }
        }
    }
}

OnResourceExecuting 中,如果結果已經在靜態欄位緩存中,Result 屬性將被設置到 context 上,同時 Action 被短路並返回緩存的結果。在 OnResourceExecuted 方法中,如果當前其請求的鍵未被使用過,那麼 Result 就會被保存到緩存中,用於之後的請求。

如下所示,把這個過濾器用於類或方法之上:

[TypeFilter(typeof(NaiveCacheResourceFilterAttribute))]
public class CachedController : Controller
{
    public IActionResult Index()
    {
        return Content("This content was generated at " + DateTime.Now);
    }
}

Action 過濾器

Action 過濾器 要麼實現 IActionFilter 介面,要麼實現 IAsyncActionFilter 介面,它們可以在 action 方法執行的前後被執行。Action 過濾器非常適合放置諸如查看模型綁定結果、或是修改控制器或輸入到 action 方法的邏輯。另外,action 過濾器可以查看並直接修改 action 方法的結果。

OnActionExecuting 方法在 action 方法執行之前運行,因此它可以通過改變 ActionExecutingContext.ActionArguments 來控制 action 的輸入,或是通過 ActionExecutingContext.Controller 控制控制器(Controller)。OnActionExecuting 方法可以通過設置 ActionExecutingContext.Result 來短路 action 方法的操作及其後續的過濾器。OnActionExecuting 方法通過拋出異常也可以阻止 action 方法和後續過濾器的處理,但會當做失敗(而不是成功)的結果來處理。

OnActionExecuted 方法在 action 方法執行之後才執行,並且可以通過 ActionExecutedContext.Result 屬性查看或控制 action 的結果。如果 action 在執行時被其它過濾器短路,則 ActionExecutedContext.Canceled 將會被置為 true。如果 action 或後續的 action 過濾器拋出異常,則 ActionExecutedContext.Exception 會被設置為一個非空值。有效「處理」完異常後把 ActionExecutedContext.Exception 設置為 null,那麼 ActionExectedContext.Result 會像從 action 方法正常返回值那樣被處理。

對於 IAsyncActionFilter 介面來說,它的 OnActionExecutionAsync 方法結合了 OnActionExecutingOnActionExecuted 的所有能力。調用 await next() 後,ActionExecutionDelegate 將會執行所有的後續 action 過濾器以及 action 方法,並返回 ActionExecutedContext
如果想要在 OnActionExecutionAsync 內部短路,那麼就為 ActionExecutingContext.Result 分配一個結果實例,並且不要調用 ActionExecutionDelegate 即可。

異常過濾器

異常過濾器 實現了 IExceptionFilter 介面或 IAsyncExceptionFilter 介面。

異常過濾器用於處理「未處理異常」,包括發生在 Controller 創建及 模型綁定 期間出現的異常。它們只在管道內發生異常時才會被調用。它們提供了一個單一的位置實現應用程式內的公共異常處理策略。框架提供了抽象的 ExceptionFilterAttribute ,你根據自己的需要繼承這個類。異常過濾器適用於捕獲 MVC Action 內出現的異常,但它們不及錯誤處理中間件(error handling middleware)靈活。一般來講優先使用中間件,只有在需要做一些基於所選 MVC Action 的、有別於錯誤處理的工作時才選擇使用過濾器。

提示
對於應用程式中不同 action 需要使用不同的錯誤處理方式,並向 Views/HTML 暴露 API 端點或 action 的錯誤處理結果。API 端點用 JSON 返回錯誤信息,而基於視圖的 action 則返回錯誤頁面(HTML 頁面)。

異常過濾器不應有兩個事件(對於前置或後置而言),它們只實現 OnException (或 OnExceptionAsync )。以參數形式傳入 OnExceptionExceptionContext 包含了所發生的 Exception。如果把 context.Exception 設置為 null,其效果相當於你已處理該異常,所以該次請求會像沒發生過異常那樣繼續處理(一般會返回 HTTP 200 OK 狀態)。下例過濾器中使用定製的開發者錯誤視圖來顯示開發環境中應用程式所出現異常的詳細信息:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;

namespace FiltersSample.Filters
{
    public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
    {
        private readonly IHostingEnvironment _hostingEnvironment;
        private readonly IModelMetadataProvider _modelMetadataProvider;

        public CustomExceptionFilterAttribute(
            IHostingEnvironment hostingEnvironment,
            IModelMetadataProvider modelMetadataProvider)
        {
            _hostingEnvironment = hostingEnvironment;
            _modelMetadataProvider = modelMetadataProvider;
        }

        public override void OnException(ExceptionContext context)
        {
            if (!_hostingEnvironment.IsDevelopment())
            {
                // do nothing
                return;
            }
            var result = new ViewResult {ViewName = "CustomError"};
            result.ViewData = new ViewDataDictionary(_modelMetadataProvider,context.ModelState);
            result.ViewData.Add("Exception", context.Exception);
            // TODO: Pass additional detailed data via ViewData
            context.ExceptionHandled = true; // mark exception as handled
            context.Result = result;
        }
    }
}

結果過濾器

實現了 IResultFilterIAsyncResultFilter 介面的 結果過濾器 在 Action Result 執行體的周圍執行。當 Action 或 Action 過濾器產生 Action 結果時,只有成功運行的才會執行結果過濾器。如果異常過濾器處理了異常,那麼結果過濾器就不會運行——除非異常過濾器將異常設置為null(Exception = null)。

註解
正在執行的結果種類取決於相關 Action。MVC Action 所返回的 View 將包含 Razor(將其作為正在處理的 ViewResult 的一部分)。API 方法則將執行一些序列化工作作為其執行結果的一部分。瞭解更多請移步 action 結果

結果過濾器適用於任何需要直接環繞 View 或格式化處理的邏輯。結果過濾器可以替換或更改 Action 結果(而後者負責產生響應)。

OnResultExecuting 方法運行於 Action 結果執行之前,故其可通過 ResultExecutingContext.Result 操作 Action 結果。如果將 ResultExecutingContext.Cancel 設置為 true,則 OnResultExecuting 方法可短路 Action 結果以及後續結果過濾器的執行。如果發生了短路,MVC 將不會修改響應,所以當發生短路時,為避免生成空響應,你一般應該直接去修改響應對象。如果在 OnResultExecuting 方法內拋出異常,那麼也將阻止 Action 結果以及後續過濾器的執行,但會被當做失敗結果(而非成功結果)。

OnResultExecuted 方法運行於 Action 結果執行之後。也就是說,如果沒有拋出異常,響應可能就會被髮送到客戶端且不可再修改。如果 Action 結果在執行中被其它過濾器短路,則 ResultExecutedContext.Canceled 將被置為 true。如果 Action 結果或後續結果過濾器拋出異常,則 ResultExecutedContext.Exception 將被置為非空值(non-null value)。把 ResultExecutedContext.Exception 設置為 null 後會影響到異常的“處理”,這將阻止異常在之後的管道內被 MVC 重新拋出。如果在結果過濾器內處理異常,需要確定此處是否適合將某些數據寫入響應中。如果 Action 結果在執行中途拋出異常,而 header 也已被更新到客戶端,那麼將沒有任何可靠的機制來發送失敗代碼。

對於 IAsyncResultFilterOnResultExecutionAsync 方法來講,它具有 OnResultExecutingOnResultExecuted 的功能。在 ResultExecutionDelegate 上調用 await next() 將執行後續的結果過濾器和 Action 結果,並返回 ResultExecutedContext。如果將 ResultExecutingContext.Cancel 值為 true 並不調用 ResultExectionDelegate,則將在內部短路 OnResultExecutionAsync

你可以覆蓋內建的 ResultFilterAttribute 特性,創建定製的結果過濾器, AddHeaderAttribute 類便是一例結果過濾器。

提示
若你需要為響應增加 header,在 Action 結果執行前如是做。否則響應就會被髮送到客戶端,屆時改之晚矣。故對於結果過濾器而言,為響應增加 header 需要在 OnResultExecuting 中處理(而不是在 OnResultExecuted 中)。

過濾器對比中間件

一般情況下,過濾器用於處理業務與應用程式的橫切關註點。它的用法很像 中間件 。從能力上來講過濾器酷似中間件,但過濾器的作用範圍很大,因此允許你將它插入到應用程式中需要使用到它的場合中,比如在視圖之前或在模型綁定之後。過濾器是 MVC 的一部分,可以訪問 MVC 的上下文以及構造函數。比方說,中間件不能簡單地直接察覺請求中模型驗證是否生成了錯誤並對此作出響應,而過濾器卻能做到。

如果想要嘗試一下過濾器,可以下載、測試並修改樣例


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

-Advertisement-
Play Games
更多相關文章
  • 基於OpenSLL的RSA加密應用(非演算法) === iOS開發中的小伙伴應該是經常用der和p12進行加密解密,而且在通常加密不止一種加密演算法,還可以加點兒鹽吧~本文章主要闡述的是在iOS中基於openSLL的RSA加密。一共有兩種方式,一種是基於p12加密解密的,還有一種是博客園官方提供的公鑰字 ...
  • 說明 Hybrid模式原生和H5交互原理 目錄 前言 參考來源 前置技術要求 楔子 Android、iOS原生和H5的基本通信機制 Android端 iOS端 原生和H5的另一種通訊方式:JSBridge 什麼是JSBridge 為什麼要用JSBridge JSBridge原理以及實現 前言 參考來 ...
  • 大家或許有遇到這個神坑,在Fragment中使用startActivityForResult能夠成功,可是在Fragment中的onActivityResult卻無法被調用。一不註意就讓人一夜愁白了頭。苦經探索(當然包括親愛的百度和谷歌),終於總結出了一些規律。 在Fragment中使用startA ...
  • 此文,將嘗試動態從某個不確定的文件夾中載入資源文件.文章,會繼續完善自定義的 imageNamed 函數,併為下一篇文章鋪墊. ...
  • Android的UI訪問是沒有加鎖的,這樣在多個線程訪問UI是不安全的。所以Android中規定只能在UI線程中訪問UI。 但是有沒有極端的情況?使得我們在子線程中訪問UI也可以使程式跑起來呢?接下來我們用一個例子去證實一下。 新建一個工程,activity_main.xml佈局如下所示: 很簡單, ...
  • 最前面的話:Smobiler是一個在VS環境中使用.Net語言來開發APP的開發平臺,也許比Xamarin更方便 一、目標樣式 我們要實現上圖中的效果,需要如下的操作: 1.從工具欄上的“Smobiler Components”拖動一個GaugeView控制項到窗體界面上 2.修改GaugeView控 ...
  • ASP.NET Core MVC 控制器應通過它們的構造器明確的請求它們的依賴關係。在某些情況下,單個控制器的操作可能需要一個服務,在控制器級別上的請求可能沒有意義。在這種情況下,你也可以選擇將服務作為 action 方法的參數。 ...
  • VS "15" 預覽 5 給 VB 帶來了更新。 * 值元組 ValueTuple * 二進位數字和數字分隔符 * 支持使用 ByRef 返回類型 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...