Фильтры в ASP.NET CoreFilters in ASP.NET Core

Авторы: Кирк Ларкин (Kirk Larkin) Рик Андерсон (Rick Anderson), Том Дайкстра (Tom Dykstra) и Стив Смит (Steve Smith)By Kirk Larkin, Rick Anderson, Tom Dykstra, and Steve Smith

Фильтры в ASP.NET Core позволяют выполнять код до или после определенных этапов в конвейере обработки запросов.Filters in ASP.NET Core allow code to be run before or after specific stages in the request processing pipeline.

Встроенные фильтры обрабатывают следующие задачи:Built-in filters handle tasks such as:

  • Авторизация (предотвращение несанкционированного доступа к ресурсам).Authorization (preventing access to resources a user isn't authorized for).
  • Кэширование откликов (замыкание конвейера обработки запросов для возврата кэшированного ответа).Response caching (short-circuiting the request pipeline to return a cached response).

Для обработки сквозной функциональности можно создавать настраиваемые фильтры.Custom filters can be created to handle cross-cutting concerns. Примеры сквозной функциональности включают обработку ошибок, кэширование, конфигурирование, авторизацию и ведение журнала.Examples of cross-cutting concerns include error handling, caching, configuration, authorization, and logging. Фильтры предотвращают дублирование кода.Filters avoid duplicating code. Например, можно объединить обработку ошибок с помощью фильтра исключений обработки ошибок.For example, an error handling exception filter could consolidate error handling.

Этот документ применяется к Razor страницам, контроллерам API и контроллерам с представлениями.This document applies to Razor Pages, API controllers, and controllers with views. Фильтры не работают непосредственно с Razor компонентами.Filters don't work directly with Razor components. Фильтр может влиять на компонент только косвенно в таких случаях:A filter can only indirectly affect a component when:

  • Компонент внедряется в страницу или представление.The component is embedded in a page or view.
  • Страница, контроллер или представление использует фильтр.The page or controller/view uses the filter.

Просмотреть или скачать пример (как скачивать).View or download sample (how to download).

Как работают фильтрыHow filters work

Фильтры выполняются в конвейере вызова действий ASP.NET Core, который иногда называют конвейером фильтров.Filters run within the ASP.NET Core action invocation pipeline, sometimes referred to as the filter pipeline. Конвейер фильтров запускается после того, как платформа ASP.NET Core выбирает выполняемое действие.The filter pipeline runs after ASP.NET Core selects the action to execute.

Запрос обрабатывается с помощью прочего ПО промежуточного слоя, ПО промежуточного слоя маршрутизации, выбора действия и конвейера вызова действий.

Типы фильтровFilter types

Фильтр каждого типа выполняется на определенном этапе конвейера фильтров:Each filter type is executed at a different stage in the filter pipeline:

  • Фильтры авторизации. Применяются в первую очередь и служат для определения того, авторизован ли пользователь для выполнения запроса.Authorization filters run first and are used to determine whether the user is authorized for the request. Эти фильтры могут сократить выполнение конвейера, если запрос не авторизован.Authorization filters short-circuit the pipeline if the request is not authorized.

  • Фильтры ресурсов:Resource filters:

    • Выполняются после авторизации.Run after authorization.
    • OnResourceExecuting выполняет код до остальной части конвейера фильтров.OnResourceExecuting runs code before the rest of the filter pipeline. Например, OnResourceExecuting выполняет код до привязки модели.For example, OnResourceExecuting runs code before model binding.
    • OnResourceExecuted выполняет код после завершения остальной части конвейера.OnResourceExecuted runs code after the rest of the pipeline has completed.
  • Фильтры действий:Action filters:

    • выполняют код непосредственно до и после вызова метода действия.Run code immediately before and after an action method is called.
    • Могут изменять аргументы, передаваемые в действие.Can change the arguments passed into an action.
    • Могут изменять результат, возвращенный действием.Can change the result returned from the action.
    • Не поддерживаются на Razor страницах.Are not supported in Razor Pages.
  • Фильтры исключений применяют глобальные политики к необработанным исключениям, которые происходят до записи данных в тело ответа.Exception filters apply global policies to unhandled exceptions that occur before the response body has been written to.

  • Фильтры результатов выполняют код непосредственно до и после выполнения результатов действия.Result filters run code immediately before and after the execution of action results. Они выполняются только в том случае, если метод действия выполнен успешно.They run only when the action method has executed successfully. Они полезны для логики, которая должна включать исключения для представлений или модуля форматирования.They are useful for logic that must surround view or formatter execution.

На приведенной ниже схеме показано, как типы фильтров взаимодействуют друг с другом в конвейере фильтров.The following diagram shows how filter types interact in the filter pipeline.

Запрос обрабатывается посредством фильтров авторизации, фильтров ресурсов, привязки модели, фильтров действий, выполнения действия и преобразования результата действия, фильтров исключений, фильтров результатов и выполнения результатов.

РеализацияImplementation

Фильтры поддерживают как синхронные, так и асинхронные реализации посредством различных определений интерфейсов.Filters support both synchronous and asynchronous implementations through different interface definitions.

Синхронные фильтры выполняют код до и после этапа конвейера.Synchronous filters run code before and after their pipeline stage. Например, OnActionExecuting вызывается перед вызовом метода действия,For example, OnActionExecuting is called before the action method is called. а OnActionExecuted — после возврата метода действия.OnActionExecuted is called after the action method returns.

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

В приведенном выше коде мидебуг — это служебная функция в примере загрузки.In the preceding code, MyDebug is a utility function in the sample download.

Асинхронные фильтры определяют метод On-Stage-ExecutionAsync.Asynchronous filters define an On-Stage-ExecutionAsync method. Например, OnActionExecutionAsync:For example, OnActionExecutionAsync:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // Do something before the action executes.

        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

В приведенном выше коде SampleAsyncActionFilter включает ActionExecutionDelegate (next) для выполнения метода действия.In the preceding code, the SampleAsyncActionFilter has an ActionExecutionDelegate (next) that executes the action method.

Несколько этапов фильтраMultiple filter stages

Реализовать интерфейсы для нескольких этапов фильтра можно в одном классе.Interfaces for multiple filter stages can be implemented in a single class. Например, класс ActionFilterAttribute реализует следующие интерфейсы:For example, the ActionFilterAttribute class implements:

Реализуйте синхронный или асинхронный интерфейс фильтра, но не оба варианта.Implement either the synchronous or the async version of a filter interface, not both. Среда выполнения сначала проверяет, реализует ли фильтр асинхронный интерфейс. Если да, вызывается именно он.The runtime checks first to see if the filter implements the async interface, and if so, it calls that. В противном случае вызываются методы синхронного интерфейса.If not, it calls the synchronous interface's method(s). Если асинхронный и синхронный интерфейсы реализованы в одном классе, вызывается только асинхронный метод.If both asynchronous and synchronous interfaces are implemented in one class, only the async method is called. При использовании абстрактных классов ActionFilterAttribute , таких как, переопределяйте только синхронные методы или асинхронные методы для каждого типа фильтра.When using abstract classes like ActionFilterAttribute, override only the synchronous methods or the asynchronous methods for each filter type.

Встроенные атрибуты фильтровBuilt-in filter attributes

ASP.NET Core включает встроенные фильтры на основе атрибутов, с помощью которых можно создавать подклассы и которые можно настраивать.ASP.NET Core includes built-in attribute-based filters that can be subclassed and customized. Например, следующий фильтр результатов добавляет заголовок к ответу:For example, the following result filter adds a header to the response:

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);
    }
}

Атрибуты позволяют фильтрам принимать аргументы, как показано в примере выше.Attributes allow filters to accept arguments, as shown in the preceding example. Примените AddHeaderAttribute к методу контроллера или действия, а затем укажите имя и значение заголовка HTTP:Apply the AddHeaderAttribute to a controller or action method and specify the name and value of the HTTP header:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

Для изучения заголовков используйте средство разработчика браузера .Use a tool such as the browser developer tools to examine the headers. В заголовке ответа отображается author: Rick Anderson.Under Response Headers, author: Rick Anderson is displayed.

В следующем коде реализуется класс ActionFilterAttribute, который выполняет следующие действия:The following code implements an ActionFilterAttribute that:

  • Считывает заголовок и имя в системе конфигурации.Reads the title and name from the configuration system. В отличие от предыдущего примера, для указанного ниже кода не нужно добавлять параметры фильтра.Unlike the previous sample, the following code doesn't require filter parameters to be added to the code.
  • Добавляет заголовок и имя к заголовку ответа.Adds the title and name to the response header.
public class MyActionFilterAttribute : ActionFilterAttribute
{
    private readonly PositionOptions _settings;

    public MyActionFilterAttribute(IOptions<PositionOptions> options)
    {
        _settings = options.Value;
        Order = 1;
    }

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

Параметры конфигурации предоставляются из системы конфигурации с помощью шаблона параметров.The configuration options are provided from the configuration system using the options pattern. Например, из файла appsettings.json:For example, from the appsettings.json file:

{
    "Position": {
        "Title": "Редактор",
        "Name": "Joe Smith"
    },
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "AllowedHosts": "*"
}

В StartUp.ConfigureServices:In the StartUp.ConfigureServices:

  • Класс PositionOptions добавляется в контейнер службы с помощью области конфигурации "Position".The PositionOptions class is added to the service container with the "Position" configuration area.
  • MyActionFilterAttribute добавляется в контейнер службы.The MyActionFilterAttribute is added to the service container.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
             Configuration.GetSection("Position"));
    services.AddScoped<MyActionFilterAttribute>();

    services.AddControllersWithViews();
}

В следующем коде демонстрируется класс PositionOptions:The following code shows the PositionOptions class:

public class PositionOptions
{
    public string Title { get; set; }
    public string Name { get; set; }
}

В следующем коде применяется атрибут MyActionFilterAttribute к методу Index2:The following code applies the MyActionFilterAttribute to the Index2 method:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

В разделе заголовки ответов, author: Rick Anderson и Editor: Joe Smith отображается при Sample/Index2 вызове конечной точки.Under Response Headers, author: Rick Anderson, and Editor: Joe Smith is displayed when the Sample/Index2 endpoint is called.

Следующий код применяет MyActionFilterAttribute и AddHeaderAttribute к Razor странице:The following code applies the MyActionFilterAttribute and the AddHeaderAttribute to the Razor Page:

[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Фильтры не могут применяться к Razor методам обработчика страниц.Filters cannot be applied to Razor Page handler methods. Их можно применять либо к Razor модели страницы, либо глобально.They can be applied either to the Razor Page model or globally.

Некоторые интерфейсы фильтров имеют соответствующие атрибуты, которые можно использовать как базовые классы для пользовательских реализаций.Several of the filter interfaces have corresponding attributes that can be used as base classes for custom implementations.

Атрибуты фильтров:Filter attributes:

Области и порядок выполнения фильтровFilter scopes and order of execution

Фильтр можно добавить в конвейер в одной из трех областей:A filter can be added to the pipeline at one of three scopes:

  • С помощью атрибута в действии контроллера.Using an attribute on a controller action. Атрибуты фильтра не могут применяться к Razor методам обработчика страниц.Filter attributes cannot be applied to Razor Pages handler methods.
  • Использование атрибута на контроллере или Razor странице.Using an attribute on a controller or Razor Page.
  • Глобально для всех контроллеров, действий и Razor страниц, как показано в следующем коде:Globally for all controllers, actions, and Razor Pages as shown in the following code:
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

Порядок выполнения по умолчаниюDefault order of execution

Если для определенного этапа конвейера имеется несколько фильтров, область определяет порядок их выполнения по умолчанию.When there are multiple filters for a particular stage of the pipeline, scope determines the default order of filter execution. Глобальные фильтры заключают в себя фильтры классов, которые, в свою очередь, заключают в себя фильтры методов.Global filters surround class filters, which in turn surround method filters.

В результате такого вложения последующий код фильтров выполняется в порядке, обратном выполнению предшествующего кода.As a result of filter nesting, the after code of filters runs in the reverse order of the before code. Последовательность фильтров:The filter sequence:

  • Предшествующий код глобальных фильтров.The before code of global filters.
    • Код, предшествующий контроллеру и Razor фильтрам страниц.The before code of controller and Razor Page filters.
      • Предшествующий код фильтров методов действий.The before code of action method filters.
      • Последующий код фильтров методов действий.The after code of action method filters.
    • Код после кода фильтров контроллеров и Razor страниц.The after code of controller and Razor Page filters.
  • Последующий код глобальных фильтров.The after code of global filters.

В следующем примере показан порядок вызова методов фильтров для синхронных фильтров действий.The following example that illustrates the order in which filter methods are called for synchronous action filters.

SequenceSequence Область фильтраFilter scope Метод фильтраFilter method
11 ГлобальныйGlobal OnActionExecuting
22 Контроллер или Razor страницаController or Razor Page OnActionExecuting
33 МетодMethod OnActionExecuting
44 МетодMethod OnActionExecuted
55 Контроллер или Razor страницаController or Razor Page OnActionExecuted
66 ГлобальныйGlobal OnActionExecuted

Фильтры на уровне контроллераController level filters

Каждый контроллер, наследуемый от базового класса Controller включает методы Controller.OnActionExecuting, Controller.OnActionExecutionAsync и Controller.OnActionExecuted OnActionExecuted.Every controller that inherits from the Controller base class includes Controller.OnActionExecuting, Controller.OnActionExecutionAsync, and Controller.OnActionExecuted OnActionExecuted methods. Эти методы:These methods:

  • Заключают фильтры, которые выполняются для указанного действия.Wrap the filters that run for a given action.
  • OnActionExecuting вызывается перед всеми фильтрами действий.OnActionExecuting is called before any of the action's filters.
  • OnActionExecuted вызывается после всех фильтров действий.OnActionExecuted is called after all of the action filters.
  • OnActionExecutionAsync вызывается перед всеми фильтрами действий.OnActionExecutionAsync is called before any of the action's filters. Код в фильтре после next выполняется после метода действия.Code in the filter after next runs after the action method.

Например, в скачиваемом примере MySampleActionFilter применяется глобально при запуске.For example, in the download sample, MySampleActionFilter is applied globally in startup.

TestController:The TestController:

  • Применяет SampleActionFilterAttribute ([SampleActionFilter]) для действия FilterTest2.Applies the SampleActionFilterAttribute ([SampleActionFilter]) to the FilterTest2 action.
  • Переопределяет OnActionExecuting и OnActionExecuted.Overrides OnActionExecuting and OnActionExecuted.
public class TestController : Controller
{
    [SampleActionFilter(Order = int.MinValue)]
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

MyDisplayRouteInfo предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.MyDisplayRouteInfo is provided by the Rick.Docs.Samples.RouteInfo NuGet package and displays route information.

Переходит к https://localhost:5001/Test2/FilterTest2 для выполнения следующего кода:Navigating to https://localhost:5001/Test2/FilterTest2 runs the following code:

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

Фильтры на уровне контроллера задают для свойства Order значение int.MinValue.Controller level filters set the Order property to int.MinValue. Их нельзя настроить, чтобы они запускались после применения фильтров к методам.Controller level filters can not be set to run after filters applied to methods. Свойство Order рассматривается в следующем разделе.Order is explained in the next section.

Для Razor страниц см. раздел реализация Razor фильтров страниц путем переопределения методов фильтров.For Razor Pages, see Implement Razor Page filters by overriding filter methods.

Переопределение порядка по умолчаниюOverriding the default order

Чтобы переопределить порядок выполнения по умолчанию, можно реализовать IOrderedFilter.The default sequence of execution can be overridden by implementing IOrderedFilter. IOrderedFilter предоставляет свойство Order, которое имеет приоритет над областью и определяет порядок выполнения.IOrderedFilter exposes the Order property that takes precedence over scope to determine the order of execution. Фильтр со значением меньше Order:A filter with a lower Order value:

  • Выполняется перед кодом, выполняемым до фильтра с более высоким значением Order.Runs the before code before that of a filter with a higher value of Order.
  • Выполняется после кода, выполняемого после фильтра с более высоким значением Order.Runs the after code after that of a filter with a higher Order value.

Свойство Order задается с помощью параметра конструктора:The Order property is set with a constructor parameter:

[SampleActionFilter(Order = int.MinValue)]

Рассмотрим два фильтра действий в следующем контроллере:Consider the two action filters in the following controller:

[MyAction2Filter]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Глобальный фильтр добавляется в StartUp.ConfigureServices:A global filter is added in StartUp.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

3 фильтра выполняются в следующем порядке:The 3 filters run in the following order:

  • Test2Controller.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • MyAction2FilterAttribute.OnActionExecuting
        • Test2Controller.FilterTest2
      • MySampleActionFilter.OnActionExecuted
    • MyAction2FilterAttribute.OnResultExecuting
  • Test2Controller.OnActionExecuted

Свойство Order переопределяет область при определении порядка выполнения фильтров.The Order property overrides scope when determining the order in which filters run. Фильтры сначала сортируются по порядку, а затем очередность окончательно определяется по области.Filters are sorted first by order, then scope is used to break ties. Все встроенные фильтры реализуют IOrderedFilter и задают значение по умолчанию Order, равное 0.All of the built-in filters implement IOrderedFilter and set the default Order value to 0. Как упоминалось ранее, фильтры на уровне контроллера задают для свойства Order значение int.MinValue. Во встроенных фильтрах область определяет порядок, если для Order не задано ненулевое значение.As mentioned previously, controller level filters set the Order property to int.MinValue For built-in filters, scope determines order unless Order is set to a non-zero value.

В указанном выше коде MySampleActionFilter имеет глобальную область действия и запускается перед MyAction2FilterAttribute, у которого область действия контроллера.In the preceding code, MySampleActionFilter has global scope so it runs before MyAction2FilterAttribute, which has controller scope. Чтобы MyAction2FilterAttribute запускался первым, задайте для свойства Order значение int.MinValue:To make MyAction2FilterAttribute run first, set the order to int.MinValue:

[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

Чтобы глобальный фильтр MySampleActionFilter запускался первым, задайте для свойства Order значение int.MinValue:To make the global filter MySampleActionFilter run first, set Order to int.MinValue:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter),
                            int.MinValue);
    });
}

Отмена и сокращенное выполнениеCancellation and short-circuiting

Вы можете настроить сокращенное выполнение конвейера фильтров, задав свойство Result для параметра ResourceExecutingContext, передаваемого в метод фильтра.The filter pipeline can be short-circuited by setting the Result property on the ResourceExecutingContext parameter provided to the filter method. Например, приведенный ниже фильтр ресурсов предотвращает выполнение остальной части конвейера:For instance, the following Resource filter prevents the rest of the pipeline from executing:

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

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

В приведенном ниже коде как фильтр ShortCircuitingResourceFilter, так и фильтр AddHeader нацелены на метод действия SomeResource.In the following code, both the ShortCircuitingResourceFilter and the AddHeader filter target the SomeResource action method. ShortCircuitingResourceFilter:The ShortCircuitingResourceFilter:

  • Выполняется первым, поскольку это фильтр ресурсов, а AddHeader — фильтр действий.Runs first, because it's a Resource Filter and AddHeader is an Action Filter.
  • Замыкает оставшуюся часть конвейера.Short-circuits the rest of the pipeline.

Поэтому фильтр AddHeader никогда не выполняется для действия SomeResource.Therefore the AddHeader filter never runs for the SomeResource action. Поведение будет аналогичным при применении обоих фильтров на уровне метода действия при условии, что фильтр ShortCircuitingResourceFilter выполняется первым.This behavior would be the same if both filters were applied at the action method level, provided the ShortCircuitingResourceFilter ran first. Фильтр ShortCircuitingResourceFilter выполняется в первую очередь из-за его типа или в связи с явным указанием свойства Order.The ShortCircuitingResourceFilter runs first because of its filter type, or by explicit use of Order property.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

Внедрение зависимостейDependency injection

Фильтры можно добавлять по типу или экземпляру.Filters can be added by type or by instance. Добавляемый экземпляр используется для каждого запроса.If an instance is added, that instance is used for every request. Если добавляется тип, он является активированным.If a type is added, it's type-activated. Активация фильтра означает, что:A type-activated filter means:

  • Экземпляр создается для каждого запроса.An instance is created for each request.
  • Зависимости конструктора заполняются путем внедрения зависимостей.Any constructor dependencies are populated by dependency injection (DI).

Фильтры, которые реализуются как атрибуты и добавляются непосредственно в классы контроллеров или методы действий, не могут иметь зависимости конструктора, предоставленные посредством внедрения зависимостей.Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). Зависимости конструктора не могут предоставляться путем внедрения зависимостей, так как:Constructor dependencies cannot be provided by DI because:

  • Параметры конструктора должны предоставляться для атрибутов в месте их применения.Attributes must have their constructor parameters supplied where they're applied.
  • Это ограничение, налагаемое на использование атрибутов.This is a limitation of how attributes work.

Следующие фильтры поддерживают зависимости конструктора, предоставленные путем внедрения зависимостей:The following filters support constructor dependencies provided from DI:

Предыдущие фильтры могут применяться к контроллеру или методу действия.The preceding filters can be applied to a controller or action method:

Средства ведения журнала доступны путем внедрения зависимостей.Loggers are available from DI. Но следует избегать создания и использования фильтров исключительно для целей ведения журнала.However, avoid creating and using filters purely for logging purposes. Встроенное средство ведения журнала обычно предоставляет все необходимое для ведения журнала.The built-in framework logging typically provides what's needed for logging. При добавлении ведения журналов в фильтры следует учитывать следующее:Logging added to filters:

  • Следует сосредоточиться на бизнес-среде или поведении в привязке к фильтру.Should focus on business domain concerns or behavior specific to the filter.
  • Не следует регистрировать действия или другие события платформы,Should not log actions or other framework events. так как это делают встроенные фильтры.The built-in filters log actions and framework events.

ServiceFilterAttributeServiceFilterAttribute

Типы реализации фильтра службы регистрируются в ConfigureServices.Service filter implementation types are registered in ConfigureServices. Атрибут ServiceFilterAttribute извлекает экземпляр фильтра из внедрения зависимостей.A ServiceFilterAttribute retrieves an instance of the filter from DI.

В следующем коде используется AddHeaderResultServiceFilter:The following code shows the AddHeaderResultServiceFilter:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

В следующем коде в контейнер внедрения зависимостей добавляется AddHeaderResultServiceFilter:In the following code, AddHeaderResultServiceFilter is added to the DI container:

public void ConfigureServices(IServiceCollection services)
{
    // Add service filters.
    services.AddScoped<AddHeaderResultServiceFilter>();
    services.AddScoped<SampleActionFilterAttribute>();

    services.AddControllersWithViews(options =>
   {
       options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
           "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    });
}

В следующем коде атрибут ServiceFilter извлекает экземпляр фильтра AddHeaderResultServiceFilter из внедрения зависимостей:In the following code, the ServiceFilter attribute retrieves an instance of the AddHeaderResultServiceFilter filter from DI:

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

При использовании ServiceFilterAttribute задание ServiceFilterAttribute.IsReusable:When using ServiceFilterAttribute, setting ServiceFilterAttribute.IsReusable:

  • Указывает, что экземпляр фильтра можно многократно использовать за пределами области запроса, в которой он был создан.Provides a hint that the filter instance may be reused outside of the request scope it was created within. Среда выполнения ASP.NET Core не гарантирует:The ASP.NET Core runtime doesn't guarantee:

    • что будет создан хотя бы один экземпляр фильтра;That a single instance of the filter will be created.
    • что фильтр не будет повторно запрошен из контейнера внедрения зависимостей на более позднем этапе.The filter will not be re-requested from the DI container at some later point.
  • Не следует использовать с фильтром, который зависит от служб со временем существования, кроме элементов singleton.Should not be used with a filter that depends on services with a lifetime other than singleton.

Объект ServiceFilterAttribute реализует интерфейс IFilterFactory.ServiceFilterAttribute implements IFilterFactory. IFilterFactory предоставляет метод CreateInstance для создания экземпляра IFilterMetadata.IFilterFactory exposes the CreateInstance method for creating an IFilterMetadata instance. CreateInstance загружает указанный тип из внедрения зависимостей.CreateInstance loads the specified type from DI.

TypeFilterAttributeTypeFilterAttribute

Атрибут TypeFilterAttribute похож на ServiceFilterAttribute, но его тип не разрешается напрямую из контейнера внедрения зависимостей.TypeFilterAttribute is similar to ServiceFilterAttribute, but its type isn't resolved directly from the DI container. Он создает экземпляр типа с помощью Microsoft.Extensions.DependencyInjection.ObjectFactory.It instantiates the type by using Microsoft.Extensions.DependencyInjection.ObjectFactory.

Так как типы TypeFilterAttribute не разрешаются напрямую из контейнера внедрения зависимостей:Because TypeFilterAttribute types aren't resolved directly from the DI container:

  • Типы, на которые ссылаются с помощью TypeFilterAttribute, не нужно регистрировать в контейнере внедрения зависимостей.Types that are referenced using the TypeFilterAttribute don't need to be registered with the DI container. Их зависимости выполняются самим контейнером.They do have their dependencies fulfilled by the DI container.
  • Атрибут TypeFilterAttribute может также принимать аргументы конструктора для типа.TypeFilterAttribute can optionally accept constructor arguments for the type.

При использовании TypeFilterAttribute задание TypeFilterAttribute.IsReusable:When using TypeFilterAttribute, setting TypeFilterAttribute.IsReusable:

  • Указывает, что экземпляр фильтра можно многократно использовать за пределами области запроса, в которой он был создан.Provides hint that the filter instance may be reused outside of the request scope it was created within. Среда выполнения ASP.NET Core не предоставляет никаких гарантий, что будет создан хотя бы один экземпляр фильтра.The ASP.NET Core runtime provides no guarantees that a single instance of the filter will be created.

  • Не следует использовать с фильтром, который зависит от служб со временем существования, кроме элементов singleton.Should not be used with a filter that depends on services with a lifetime other than singleton.

В следующем примере показано, как передавать аргументы в тип с помощью TypeFilterAttribute:The following example shows how to pass arguments to a type using TypeFilterAttribute:

[TypeFilter(typeof(LogConstantFilter),
    Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

Фильтры авторизацииAuthorization filters

Фильтры авторизации:Authorization filters:

  • Выполняются в первую очередь в конвейере фильтров.Are the first filters run in the filter pipeline.
  • Контролируют доступ к методам действий.Control access to action methods.
  • Имеют предшествующий, но не последующий метод.Have a before method, but no after method.

Для работы пользовательских фильтров авторизации требуется настраиваемая платформа авторизации.Custom authorization filters require a custom authorization framework. Настройка политик авторизации или определение пользовательской политики авторизации предпочтительнее создания пользовательского фильтра.Prefer configuring the authorization policies or writing a custom authorization policy over writing a custom filter. Встроенный фильтр авторизации:The built-in authorization filter:

  • Вызывает систему авторизации.Calls the authorization system.
  • Не выполняет авторизацию запросов.Does not authorize requests.

Не вызывайте исключения в фильтрах авторизации:Do not throw exceptions within authorization filters:

  • Исключение не будет обработано.The exception will not be handled.
  • Фильтры исключений не будут обрабатывать исключение.Exception filters will not handle the exception.

При возникновении исключения в фильтре авторизации попробуйте создать запрос.Consider issuing a challenge when an exception occurs in an authorization filter.

Дополнительные сведения об авторизации.Learn more about Authorization.

Фильтры ресурсовResource filters

Фильтры ресурсов:Resource filters:

Фильтры ресурсов полезны для сокращенного выполнения большей части конвейера.Resource filters are useful to short-circuit most of the pipeline. Например, фильтр кэширования предотвращает выполнение остальной части конвейера при попадании в кэше.For example, a caching filter can avoid the rest of the pipeline on a cache hit.

Примеры фильтров ресурсов:Resource filter examples:

Фильтры действийAction filters

Фильтры действий не применяются к Razor страницам.Action filters do not apply to Razor Pages. Razor Страницы поддерживают IPageFilter и IAsyncPageFilter .Razor Pages supports IPageFilter and IAsyncPageFilter . Дополнительные сведения см. в разделе Методы фильтрации для Razor Pages.For more information, see Filter methods for Razor Pages.

Фильтры действий:Action filters:

  • Реализуют либо интерфейс IActionFilter, либо интерфейс IAsyncActionFilter.Implement either the IActionFilter or IAsyncActionFilter interface.
  • Их выполнение охватывает выполнение методов действия.Their execution surrounds the execution of action methods.

В следующем фрагменте кода показан пример фильтра действий:The following code shows a sample action filter:

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

ActionExecutingContext предоставляет следующие свойства:The ActionExecutingContext provides the following properties:

  • ActionArguments позволяет считать входные данные в метод действия.ActionArguments - enables reading the inputs to an action method.
  • Controller позволяет управлять экземпляром контроллера.Controller - enables manipulating the controller instance.
  • Result позволяет при задании свойства Result сократить выполнение метода действия и последующих фильтров действий.Result - setting Result short-circuits execution of the action method and subsequent action filters.

Вызов исключения в методе действия:Throwing an exception in an action method:

  • Предотвращает выполнение последующих фильтров.Prevents running of subsequent filters.
  • В отличие от параметра Result рассматривается как сбой, а не успешный результат.Unlike setting Result, is treated as a failure instead of a successful result.

ActionExecutedContext предоставляет Controller и Result, а также следующие свойства:The ActionExecutedContext provides Controller and Result plus the following properties:

  • Canceled будет иметь значение true, если выполнение действия было сокращено другим фильтром.Canceled - True if the action execution was short-circuited by another filter.

  • Exception будет иметь ненулевое значение, если предыдущее выполнение фильтра действий вызвало исключение.Exception - Non-null if the action or a previously run action filter threw an exception. При присвоении этому свойству ненулевого значения:Setting this property to null:

    • Будет эффективно обрабатываться исключение.Effectively handles the exception.
    • Result будет выполняться так, как при возвращении методом действия.Result is executed as if it was returned from the action method.

Для IAsyncActionFilter вызов ActionExecutionDelegate:For an IAsyncActionFilter, a call to the ActionExecutionDelegate:

  • Приводит к выполнению последующих фильтров действий и метода действия.Executes any subsequent action filters and the action method.
  • Возвращает ActionExecutedContext.Returns ActionExecutedContext.

Для сокращенного выполнения присвойте Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result экземпляру результата и не вызывайте next (ActionExecutionDelegate).To short-circuit, assign Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result to a result instance and don't call next (the ActionExecutionDelegate).

Платформа предоставляет абстрактный класс ActionFilterAttribute, для которого можно создавать подклассы.The framework provides an abstract ActionFilterAttribute that can be subclassed.

Фильтр действий OnActionExecuting можно использовать:The OnActionExecuting action filter can be used to:

  • Для проверки состояния модели.Validate model state.
  • Для получения ошибки, если состояние является недопустимым.Return an error if the state is invalid.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }

Примечание

Контроллеры, отмеченные [ApiController] атрибутом, автоматически проверяют состояние модели и возвращают ответ 400.Controllers annotated with the [ApiController] attribute automatically validate model state and return a 400 response. Дополнительные сведения см. в разделе Автоматические отклики HTTP 400.For more information, see Automatic HTTP 400 responses.

Метод OnActionExecuted выполняется после метода действия:The OnActionExecuted method runs after the action method:

  • Он имеет доступ к результатам действия и может управлять ими с помощью свойства Result.And can see and manipulate the results of the action through the Result property.

  • Canceled будет иметь значение true, если выполнение действия было сокращено другим фильтром.Canceled is set to true if the action execution was short-circuited by another filter.

  • Exception будет иметь ненулевое значение, если действие или последующий фильтр действий вызвали исключение.Exception is set to a non-null value if the action or a subsequent action filter threw an exception. Если установить для Exception значение NULL:Setting Exception to null:

    • Будет эффективно обрабатываться исключение.Effectively handles an exception.
    • ActionExecutedContext.Result будет выполняться так, как если бы метод действия вернул его обычным образом.ActionExecutedContext.Result is executed as if it were returned normally from the action method.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }


    public override void OnActionExecuted(ActionExecutedContext 
                                          context)
    {
        var result = context.Result;
        // Do something with Result.
        if (context.Canceled == true)
        {
            // Action execution was short-circuited by another filter.
        }

        if(context.Exception != null)
        {
            // Exception thrown by action or action filter.
            // Set to null to handle the exception.
            context.Exception = null;
        }
        base.OnActionExecuted(context);
    }
}

Фильтры исключенийException filters

Фильтры исключений:Exception filters:

В следующем примере фильтра исключений используется пользовательское представление ошибок для отображения подробных сведений об исключениях, которые происходят во время разработки приложения:The following sample exception filter uses a custom error view to display details about exceptions that occur when the app is in development:

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly IWebHostEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

    public CustomExceptionFilter(
        IWebHostEnvironment hostingEnvironment,
        IModelMetadataProvider modelMetadataProvider)
    {
        _hostingEnvironment = hostingEnvironment;
        _modelMetadataProvider = modelMetadataProvider;
    }

    public void OnException(ExceptionContext context)
    {
        if (!_hostingEnvironment.IsDevelopment())
        {
            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.Result = result;
    }
}

В следующем коде проверяется фильтр исключений:The following code tests the exception filter:

[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
    [AddHeader("Failing Controller", 
               "Won't appear when exception is handled")]
    public IActionResult Index()
    {
        throw new Exception("Testing custom exception filter.");
    }
}

Фильтры исключений:Exception filters:

  • Не имеют предшествующих и последующих событий.Don't have before and after events.
  • Реализуют OnException или OnExceptionAsync.Implement OnException or OnExceptionAsync.
  • Обрабатывать необработанные исключения, происходящие при Razor создании страницы или контроллера, Привязка модели, фильтры действий или методы действий.Handle unhandled exceptions that occur in Razor Page or controller creation, model binding, action filters, or action methods.
  • Не перехватывают исключения, которые возникают в фильтрах ресурсов, фильтрах результатов или при выполнении результата MVC.Do not catch exceptions that occur in resource filters, result filters, or MVC result execution.

Для обработки исключения присвойте свойству ExceptionHandled значение true или напишите ответ.To handle an exception, set the ExceptionHandled property to true or write a response. Это предотвратит распространение исключения.This stops propagation of the exception. Фильтр исключений не может преобразовать исключение в успешное выполнение.An exception filter can't turn an exception into a "success". Это может сделать только фильтр действий.Only an action filter can do that.

Фильтры исключений:Exception filters:

  • Хорошо подходят для перехвата исключений, возникающих в действиях.Are good for trapping exceptions that occur within actions.
  • Не обладает такой гибкостью, как ПО промежуточного слоя обработки ошибок.Are not as flexible as error handling middleware.

ПО промежуточного слоя хорошо подходит для обработки исключений.Prefer middleware for exception handling. Используйте фильтры исключений, только если обработка ошибок осуществляется по-разному с учетом вызванного метода действия.Use exception filters only where error handling differs based on which action method is called. Например, в приложении могут использоваться методы действий как для конечных точек API, так и для представлений или HTML.For example, an app might have action methods for both API endpoints and for views/HTML. Конечные точки API могут возвращать сведения об ошибках в формате JSON, в то время как действия на основе представлений могут возвращать страницу ошибки в формате HTML.The API endpoints could return error information as JSON, while the view-based actions could return an error page as HTML.

Фильтры результатовResult filters

Фильтры результатов:Result filters:

IResultFilter и IAsyncResultFilterIResultFilter and IAsyncResultFilter

В следующем фрагменте кода показан фильтр результатов, который добавляет заголовок HTTP:The following code shows a result filter that adds an HTTP header:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

Тип выполняемого результата зависит от соответствующего действия.The kind of result being executed depends on the action. Действие, возвращающее представление, включает всю обработку Razor в рамках выполняемого объекта ViewResult.An action returning a view includes all razor processing as part of the ViewResult being executed. В процессе выполнения результата метод API может производить сериализацию.An API method might perform some serialization as part of the execution of the result. Дополнительные сведения о результатах действий.Learn more about action results.

Фильтры результатов выполняются только в том случае, когда действие или фильтры действий предоставляют результат действия.Result filters are only executed when an action or action filter produces an action result. Фильтры результатов не выполняются в следующих случаях:Result filters are not executed when:

  • Фильтр авторизации или ресурсов выполняет сокращение конвейера.An authorization filter or resource filter short-circuits the pipeline.
  • Фильтр исключений обрабатывает исключение и выдает результат действия.An exception filter handles an exception by producing an action result.

Метод Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting может сокращать выполнение результата действия и последующих фильтров результатов, присваивая свойству Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel значение true.The Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting method can short-circuit execution of the action result and subsequent result filters by setting Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel to true. При сокращении выполнения выполните запись в объект ответа, чтобы избежать формирования пустого ответа.Write to the response object when short-circuiting to avoid generating an empty response. Создание исключения в IResultFilter.OnResultExecuting:Throwing an exception in IResultFilter.OnResultExecuting:

  • предотвращает выполнение результата действия и последующих фильтров;Prevents execution of the action result and subsequent filters.
  • рассматривается как сбой, а не успешный результат.Is treated as a failure instead of a successful result.

Если запускается метод Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted, ответ, скорее всего, уже был отправлен клиенту.When the Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted method runs, the response has probably already been sent to the client. Если ответ уже был отправлен клиенту, его нельзя изменить.If the response has already been sent to the client, it cannot be changed.

ResultExecutedContext.Canceled будет иметь значение true, если выполнение результата действия было сокращено другим фильтром.ResultExecutedContext.Canceled is set to true if the action result execution was short-circuited by another filter.

ResultExecutedContext.Exception будет иметь ненулевое значение, если результат действия или последующий фильтр результатов вызвали исключение.ResultExecutedContext.Exception is set to a non-null value if the action result or a subsequent result filter threw an exception. Присвоение Exception значения NULL приводит к обработке исключения и предотвращает его последующий вызов на дальнейших этапах конвейера.Setting Exception to null effectively handles an exception and prevents the exception from being thrown again later in the pipeline. Надежный способ записи данных в ответ при обработке исключения в фильтре результатов отсутствует.There is no reliable way to write data to a response when handling an exception in a result filter. Если результат действия вызывает исключение в процессе выполнения и заголовки уже были переданы в клиент, надежного механизма отправки кода сбоя не существует.If the headers have been flushed to the client when an action result throws an exception, there's no reliable mechanism to send a failure code.

Для IAsyncResultFilter вызов к await next для ResultExecutionDelegate приводит к выполнению последующих фильтров результатов и результата действия.For an IAsyncResultFilter, a call to await next on the ResultExecutionDelegate executes any subsequent result filters and the action result. Для сокращения выполнения задайте ResultExecutingContext.Cancel для true и не вызывайте ResultExecutionDelegate:To short-circuit, set ResultExecutingContext.Cancel to true and don't call the ResultExecutionDelegate:

public class MyAsyncResponseFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                             ResultExecutionDelegate next)
    {
        if (!(context.Result is EmptyResult))
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }

    }
}

Платформа предоставляет абстрактный класс ResultFilterAttribute, для которого можно создавать подклассы.The framework provides an abstract ResultFilterAttribute that can be subclassed. Представленный ранее класс AddHeaderAttribute — это пример атрибута фильтра результатов.The AddHeaderAttribute class shown previously is an example of a result filter attribute.

IAlwaysRunResultFilter и IAsyncAlwaysRunResultFilterIAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter

Интерфейсы IAlwaysRunResultFilter и IAsyncAlwaysRunResultFilter объявляют реализацию IResultFilter, которая выполняется для всех результатов действий.The IAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter interfaces declare an IResultFilter implementation that runs for all action results. Сюда включены результаты действий, созданные:This includes action results produced by:

  • фильтрами авторизации и фильтрами ресурсов, которые сокращают ответ;Authorization filters and resource filters that short-circuit.
  • фильтрами исключений.Exception filters.

Например, следующий фильтр всегда выполняется, задавая результат действия (ObjectResult) с кодом состояния 422 Unprocessable Entity при сбое согласования содержимого:For example, the following filter always runs and sets an action result (ObjectResult) with a 422 Unprocessable Entity status code when content negotiation fails:

public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult &&
            statusCodeResult.StatusCode == 415)
        {
            context.Result = new ObjectResult("Can't process this!")
            {
                StatusCode = 422,
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

IFilterFactoryIFilterFactory

Объект IFilterFactory реализует интерфейс IFilterMetadata.IFilterFactory implements IFilterMetadata. Поэтому экземпляр IFilterFactory можно использовать в качестве экземпляра IFilterMetadata в любом месте конвейера фильтров.Therefore, an IFilterFactory instance can be used as an IFilterMetadata instance anywhere in the filter pipeline. Когда среда выполнения готовится вызвать фильтр, она пытается привести его к IFilterFactory.When the runtime prepares to invoke the filter, it attempts to cast it to an IFilterFactory. Если приведение проходит успешно, вызывается метод CreateInstance для создания вызываемого экземпляра IFilterMetadata.If that cast succeeds, the CreateInstance method is called to create the IFilterMetadata instance that is invoked. Это обеспечивает высокую степень гибкости, так как при запуске приложения нет необходимости в явном и точном определении конвейера фильтров.This provides a flexible design, since the precise filter pipeline doesn't need to be set explicitly when the app starts.

Еще один подход к созданию фильтров заключается в реализации IFilterFactory с помощью настраиваемых атрибутов:IFilterFactory can be implemented using custom attribute implementations as another approach to creating filters:

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[] { "My header" });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

В следующем коде применяется фильтр:The filter is applied in the following code:

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

Проверьте приведенный выше код, выполнив скачиваемый пример:Test the preceding code by running the download sample:

  • Вызовите средства для разработчика (F12).Invoke the F12 developer tools.
  • Перейдите на страницу https://localhost:5001/Sample/HeaderWithFactory.Navigate to https://localhost:5001/Sample/HeaderWithFactory.

В средствах для разработчика (F12) отобразятся следующие заголовки ответа, добавленные примером кода:The F12 developer tools display the following response headers added by the sample code:

  • Автор:Rick Andersonauthor: Rick Anderson
  • globaladdheader: Result filter added to MvcOptions.Filtersglobaladdheader: Result filter added to MvcOptions.Filters
  • внутренний:My headerinternal: My header

Приведенный выше код создает заголовок ответа internal: My header.The preceding code creates the internal: My header response header.

Реализация IFilterFactory в атрибутеIFilterFactory implemented on an attribute

Фильтры, реализующие IFilterFactory, используются для фильтров, которые:Filters that implement IFilterFactory are useful for filters that:

  • Не требуют передачи параметров.Don't require passing parameters.
  • Содержат зависимости конструктора, которые должны заполняться путем внедрения зависимостей.Have constructor dependencies that need to be filled by DI.

Объект TypeFilterAttribute реализует интерфейс IFilterFactory.TypeFilterAttribute implements IFilterFactory. IFilterFactory предоставляет метод CreateInstance для создания экземпляра IFilterMetadata.IFilterFactory exposes the CreateInstance method for creating an IFilterMetadata instance. CreateInstance загружает указанный тип из контейнера служб (внедрение зависимостей).CreateInstance loads the specified type from the services container (DI).

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("SampleActionFilterAttribute.OnActionExecuting");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuted");
        }
    }
}

В коде ниже приведено три примера применения [SampleActionFilter]:The following code shows three approaches to applying the [SampleActionFilter]:

[SampleActionFilter]
public IActionResult FilterTest()
{
    return Content("From FilterTest");
}

[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
    return Content("From TypeFilterTest");
}

// ServiceFilter must be registered in ConfigureServices or
// System.InvalidOperationException: No service for type '<filter>'
// has been registered. Is thrown.
[ServiceFilter(typeof(SampleActionFilterAttribute))]
public IActionResult ServiceFilterTest()
{
    return Content("From ServiceFilterTest");
}

В приведенном выше коде декорирование метода с использованием [SampleActionFilter] является рекомендуемым способом применения SampleActionFilter.In the preceding code, decorating the method with [SampleActionFilter] is the preferred approach to applying the SampleActionFilter.

Использование ПО промежуточного слоя в конвейере фильтровUsing middleware in the filter pipeline

Фильтры ресурсов по принципу работы похожи на ПО промежуточного слоя тем, что они заключают в себя выполнение всех объектов, находящихся далее в конвейере.Resource filters work like middleware in that they surround the execution of everything that comes later in the pipeline. Однако фильтры отличаются от ПО промежуточного слоя тем, что они являются частью среды выполнения, а значит имеют доступ к контексту и конструкциям.But filters differ from middleware in that they're part of the runtime, which means that they have access to context and constructs.

Чтобы использовать ПО промежуточного слоя в качестве фильтра, создайте тип с методом Configure, определяющим ПО промежуточного слоя, которое нужно внедрить в конвейер фильтров.To use middleware as a filter, create a type with a Configure method that specifies the middleware to inject into the filter pipeline. В примере ниже ПО промежуточного слоя локализации применяется для определения текущих языка и региональных параметров для запроса:The following example uses the localization middleware to establish the current culture for a request:

public class LocalizationPipeline
{
    public void Configure(IApplicationBuilder applicationBuilder)
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fr")
        };

        var options = new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture(
                                       culture: "en-US", 
                                       uiCulture: "en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        };
        options.RequestCultureProviders = new[] 
            { new RouteDataRequestCultureProvider() {
                Options = options } };

        applicationBuilder.UseRequestLocalization(options);
    }
}

Используйте MiddlewareFilterAttribute для выполнения ПО промежуточного слоя:Use the MiddlewareFilterAttribute to run the middleware:

[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
    return Content(
          $"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
        + $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}

Фильтры ПО промежуточного слоя выполняются на том же этапе конвейера фильтров, что и фильтры ресурсов, перед привязкой модели и после остальной части конвейера.Middleware filters run at the same stage of the filter pipeline as Resource filters, before model binding and after the rest of the pipeline.

Дальнейшие действияNext actions

Авторы: Кирк Ларкин (Kirk Larkin) Рик Андерсон (Rick Anderson), Том Дайкстра (Tom Dykstra) и Стив Смит (Steve Smith)By Kirk Larkin, Rick Anderson, Tom Dykstra, and Steve Smith

Фильтры в ASP.NET Core позволяют выполнять код до или после определенных этапов в конвейере обработки запросов.Filters in ASP.NET Core allow code to be run before or after specific stages in the request processing pipeline.

Встроенные фильтры обрабатывают следующие задачи:Built-in filters handle tasks such as:

  • Авторизация (предотвращение несанкционированного доступа к ресурсам).Authorization (preventing access to resources a user isn't authorized for).
  • Кэширование откликов (замыкание конвейера обработки запросов для возврата кэшированного ответа).Response caching (short-circuiting the request pipeline to return a cached response).

Для обработки сквозной функциональности можно создавать настраиваемые фильтры.Custom filters can be created to handle cross-cutting concerns. Примеры сквозной функциональности включают обработку ошибок, кэширование, конфигурирование, авторизацию и ведение журнала.Examples of cross-cutting concerns include error handling, caching, configuration, authorization, and logging. Фильтры предотвращают дублирование кода.Filters avoid duplicating code. Например, можно объединить обработку ошибок с помощью фильтра исключений обработки ошибок.For example, an error handling exception filter could consolidate error handling.

Этот документ применяется к Razor страницам, контроллерам API и контроллерам с представлениями.This document applies to Razor Pages, API controllers, and controllers with views.

Просмотреть или скачать пример (как скачивать).View or download sample (how to download).

Как работают фильтрыHow filters work

Фильтры выполняются в конвейере вызова действий ASP.NET Core, который иногда называют конвейером фильтров.Filters run within the ASP.NET Core action invocation pipeline, sometimes referred to as the filter pipeline. Конвейер фильтров запускается после того, как платформа ASP.NET Core выбирает выполняемое действие.The filter pipeline runs after ASP.NET Core selects the action to execute.

Запрос обрабатывается с использованием прочего ПО промежуточного слоя, ПО промежуточного слоя маршрутизации, выбора действия и конвейера вызова действий ASP.NET Core.

Типы фильтровFilter types

Фильтр каждого типа выполняется на определенном этапе конвейера фильтров:Each filter type is executed at a different stage in the filter pipeline:

  • Фильтры авторизации. Применяются в первую очередь и служат для определения того, авторизован ли пользователь для выполнения запроса.Authorization filters run first and are used to determine whether the user is authorized for the request. Эти фильтры могут сократить выполнение конвейера, если запрос не авторизован.Authorization filters short-circuit the pipeline if the request is unauthorized.

  • Фильтры ресурсов:Resource filters:

    • Выполняются после авторизации.Run after authorization.
    • OnResourceExecuting может выполнять код до остальной части конвейера фильтров.OnResourceExecuting can run code before the rest of the filter pipeline. Например, OnResourceExecuting может выполнять код до привязки модели.For example, OnResourceExecuting can run code before model binding.
    • OnResourceExecuted может выполнять код после завершения остальной части конвейера.OnResourceExecuted can run code after the rest of the pipeline has completed.
  • Фильтры действий могут выполнять код непосредственно до и после вызова отдельного метода действия.Action filters can run code immediately before and after an individual action method is called. С их помощью можно управлять аргументами, передаваемыми в действие, и возвращаемым из него результатом.They can be used to manipulate the arguments passed into an action and the result returned from the action. Фильтры действий не поддерживаются на Razor страницах.Action filters are not supported in Razor Pages.

  • Фильтры исключений служат для применения глобальных политик к необработанным исключениям, которые происходят до записи данных в тело ответа.Exception filters are used to apply global policies to unhandled exceptions that occur before anything has been written to the response body.

  • Фильтры результатов могут выполнять код непосредственно до и после выполнения результатов отдельного действия.Result filters can run code immediately before and after the execution of individual action results. Они выполняются только в том случае, если метод действия выполнен успешно.They run only when the action method has executed successfully. Они полезны для логики, которая должна включать исключения для представлений или модуля форматирования.They are useful for logic that must surround view or formatter execution.

На приведенной ниже схеме показано, как типы фильтров взаимодействуют друг с другом в конвейере фильтров.The following diagram shows how filter types interact in the filter pipeline.

Запрос обрабатывается посредством фильтров авторизации, фильтров ресурсов, привязки модели, фильтров действий, выполнения действия и преобразования результата действия, фильтров исключений, фильтров результатов и выполнения результатов.

РеализацияImplementation

Фильтры поддерживают как синхронные, так и асинхронные реализации посредством различных определений интерфейсов.Filters support both synchronous and asynchronous implementations through different interface definitions.

Синхронные фильтры могут выполнять код до (On-Stage-Executing) и после (On-Stage-Executed) этапа конвейера.Synchronous filters can run code before (On-Stage-Executing) and after (On-Stage-Executed) their pipeline stage. Например, OnActionExecuting вызывается перед вызовом метода действия,For example, OnActionExecuting is called before the action method is called. а OnActionExecuted — после возврата метода действия.OnActionExecuted is called after the action method returns.

public class MySampleActionFilter : 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:Asynchronous filters define an On-Stage-ExecutionAsync method:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // Do something before the action executes.

        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

В приведенном выше коде SampleAsyncActionFilter включает ActionExecutionDelegate (next) для выполнения метода действия.In the preceding code, the SampleAsyncActionFilter has an ActionExecutionDelegate (next) that executes the action method. Каждый из методов On-Stage-ExecutionAsync принимает FilterType-ExecutionDelegate для выполнения этапа конвейера фильтра.Each of the On-Stage-ExecutionAsync methods take a FilterType-ExecutionDelegate that executes the filter's pipeline stage.

Несколько этапов фильтраMultiple filter stages

Реализовать интерфейсы для нескольких этапов фильтра можно в одном классе.Interfaces for multiple filter stages can be implemented in a single class. Например, класс ActionFilterAttribute реализует интерфейсы IActionFilter и IResultFilter, а также их асинхронные эквиваленты.For example, the ActionFilterAttribute class implements IActionFilter, IResultFilter, and their async equivalents.

Реализуйте синхронный или асинхронный интерфейс фильтра, но не оба варианта.Implement either the synchronous or the async version of a filter interface, not both. Среда выполнения сначала проверяет, реализует ли фильтр асинхронный интерфейс. Если да, вызывается именно он.The runtime checks first to see if the filter implements the async interface, and if so, it calls that. В противном случае вызываются методы синхронного интерфейса.If not, it calls the synchronous interface's method(s). Если асинхронный и синхронный интерфейсы реализованы в одном классе, вызывается только асинхронный метод.If both asynchronous and synchronous interfaces are implemented in one class, only the async method is called. При использовании абстрактных классов, таких как ActionFilterAttribute, следует переопределять только синхронные методы или асинхронный метод каждого типа фильтра.When using abstract classes like ActionFilterAttribute override only the synchronous methods or the async method for each filter type.

Встроенные атрибуты фильтровBuilt-in filter attributes

ASP.NET Core включает встроенные фильтры на основе атрибутов, с помощью которых можно создавать подклассы и которые можно настраивать.ASP.NET Core includes built-in attribute-based filters that can be subclassed and customized. Например, следующий фильтр результатов добавляет заголовок к ответу:For example, the following result filter adds a header to the response:

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);
    }
}

Атрибуты позволяют фильтрам принимать аргументы, как показано в примере выше.Attributes allow filters to accept arguments, as shown in the preceding example. Примените AddHeaderAttribute к методу контроллера или действия, а затем укажите имя и значение заголовка HTTP:Apply the AddHeaderAttribute to a controller or action method and specify the name and value of the HTTP header:

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

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

Некоторые интерфейсы фильтров имеют соответствующие атрибуты, которые можно использовать как базовые классы для пользовательских реализаций.Several of the filter interfaces have corresponding attributes that can be used as base classes for custom implementations.

Атрибуты фильтров:Filter attributes:

Области и порядок выполнения фильтровFilter scopes and order of execution

Фильтр можно добавить в конвейер в одной из трех областей:A filter can be added to the pipeline at one of three scopes:

  • С помощью атрибута в действии.Using an attribute on an action.
  • С помощью атрибута в контроллере.Using an attribute on a controller.
  • Глобально для всех контроллеров и действий, как показано в следующем коде:Globally for all controllers and actions as shown in the following code:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
            "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

Предыдущий код глобально добавляет три фильтра с помощью коллекции MvcOptions.Filters.The preceding code adds three filters globally using the MvcOptions.Filters collection.

Порядок выполнения по умолчаниюDefault order of execution

Если есть несколько одинаковых фильтров, область определяет порядок их выполнения по умолчанию.When there are multiple filters of the same type, scope determines the default order of filter execution. Глобальные фильтры заключают в себя фильтры классов,Global filters surround class filters. которые, в свою очередь, заключают в себя фильтры методов.Class filters surround method filters.

В результате такого вложения последующий код фильтров выполняется в порядке, обратном выполнению предшествующего кода.As a result of filter nesting, the after code of filters runs in the reverse order of the before code. Последовательность фильтров:The filter sequence:

  • Предшествующий код глобальных фильтров.The before code of global filters.
    • Предшествующий код фильтров контроллера.The before code of controller filters.
      • Предшествующий код фильтров методов действий.The before code of action method filters.
      • Последующий код фильтров методов действий.The after code of action method filters.
    • Последующий код фильтров контроллера.The after code of controller filters.
  • Последующий код глобальных фильтров.The after code of global filters.

В следующем примере показан порядок вызова методов фильтров для синхронных фильтров действий.The following example that illustrates the order in which filter methods are called for synchronous action filters.

SequenceSequence Область фильтраFilter scope Метод фильтраFilter method
11 ГлобальныйGlobal OnActionExecuting
22 КонтроллерController OnActionExecuting
33 МетодMethod OnActionExecuting
44 МетодMethod OnActionExecuted
55 КонтроллерController OnActionExecuted
66 ГлобальныйGlobal OnActionExecuted

Эта последовательность показывает:This sequence shows:

  • Фильтр метода вкладывается в фильтр контроллера.The method filter is nested within the controller filter.
  • Фильтр контроллера вкладывается в глобальный фильтр.The controller filter is nested within the global filter.

RazorФильтры уровня контроллера и страницыController and Razor Page level filters

Каждый контроллер, наследуемый от базового класса Controller включает методы Controller.OnActionExecuting, Controller.OnActionExecutionAsync и Controller.OnActionExecuted OnActionExecuted.Every controller that inherits from the Controller base class includes Controller.OnActionExecuting, Controller.OnActionExecutionAsync, and Controller.OnActionExecuted OnActionExecuted methods. Эти методы:These methods:

  • Заключают фильтры, которые выполняются для указанного действия.Wrap the filters that run for a given action.
  • OnActionExecuting вызывается перед всеми фильтрами действий.OnActionExecuting is called before any of the action's filters.
  • OnActionExecuted вызывается после всех фильтров действий.OnActionExecuted is called after all of the action filters.
  • OnActionExecutionAsync вызывается перед всеми фильтрами действий.OnActionExecutionAsync is called before any of the action's filters. Код в фильтре после next выполняется после метода действия.Code in the filter after next runs after the action method.

Например, в скачиваемом примере MySampleActionFilter применяется глобально при запуске.For example, in the download sample, MySampleActionFilter is applied globally in startup.

TestController:The TestController:

  • Применяет SampleActionFilterAttribute ([SampleActionFilter]) для действия FilterTest2.Applies the SampleActionFilterAttribute ([SampleActionFilter]) to the FilterTest2 action.
  • Переопределяет OnActionExecuting и OnActionExecuted.Overrides OnActionExecuting and OnActionExecuted.
public class TestController : Controller
{
    [SampleActionFilter]
    public IActionResult FilterTest2()
    {
        return Content($"From FilterTest2");
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        base.OnActionExecuted(context);
    }
}

Переходит к https://localhost:5001/Test/FilterTest2 для выполнения следующего кода:Navigating to https://localhost:5001/Test/FilterTest2 runs the following code:

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

Для Razor страниц см. раздел реализация Razor фильтров страниц путем переопределения методов фильтров.For Razor Pages, see Implement Razor Page filters by overriding filter methods.

Переопределение порядка по умолчаниюOverriding the default order

Чтобы переопределить порядок выполнения по умолчанию, можно реализовать IOrderedFilter.The default sequence of execution can be overridden by implementing IOrderedFilter. IOrderedFilter предоставляет свойство Order, которое имеет приоритет над областью и определяет порядок выполнения.IOrderedFilter exposes the Order property that takes precedence over scope to determine the order of execution. Фильтр со значением меньше Order:A filter with a lower Order value:

  • Выполняется перед кодом, выполняемым до фильтра с более высоким значением Order.Runs the before code before that of a filter with a higher value of Order.
  • Выполняется после кода, выполняемого после фильтра с более высоким значением Order.Runs the after code after that of a filter with a higher Order value.

Свойство Order можно задать с помощью параметра конструктора:The Order property can be set with a constructor parameter:

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

Рассмотрим три фильтра действий, показанные в предыдущем примере.Consider the same 3 action filters shown in the preceding example. Если свойство Order контроллера и глобальные фильтры имеют значения 1 и 2 соответственно, порядок выполнения инвертируется.If the Order property of the controller and global filters is set to 1 and 2 respectively, the order of execution is reversed.

SequenceSequence Область фильтраFilter scope Свойство OrderOrder property Метод фильтраFilter method
11 МетодMethod 00 OnActionExecuting
22 КонтроллерController 11 OnActionExecuting
33 ГлобальныйGlobal 22 OnActionExecuting
44 ГлобальныйGlobal 22 OnActionExecuted
55 КонтроллерController 11 OnActionExecuted
66 МетодMethod 00 OnActionExecuted

Свойство Order переопределяет область при определении порядка выполнения фильтров.The Order property overrides scope when determining the order in which filters run. Фильтры сначала сортируются по порядку, а затем очередность окончательно определяется по области.Filters are sorted first by order, then scope is used to break ties. Все встроенные фильтры реализуют IOrderedFilter и задают значение по умолчанию Order, равное 0.All of the built-in filters implement IOrderedFilter and set the default Order value to 0. Во встроенных фильтрах область определяет порядок, если для Order не задано ненулевое значение.For built-in filters, scope determines order unless Order is set to a non-zero value.

Отмена и сокращенное выполнениеCancellation and short-circuiting

Вы можете настроить сокращенное выполнение конвейера фильтров, задав свойство Result для параметра ResourceExecutingContext, передаваемого в метод фильтра.The filter pipeline can be short-circuited by setting the Result property on the ResourceExecutingContext parameter provided to the filter method. Например, приведенный ниже фильтр ресурсов предотвращает выполнение остальной части конвейера:For instance, the following Resource filter prevents the rest of the pipeline from executing:

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

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

В приведенном ниже коде как фильтр ShortCircuitingResourceFilter, так и фильтр AddHeader нацелены на метод действия SomeResource.In the following code, both the ShortCircuitingResourceFilter and the AddHeader filter target the SomeResource action method. ShortCircuitingResourceFilter:The ShortCircuitingResourceFilter:

  • Выполняется первым, поскольку это фильтр ресурсов, а AddHeader — фильтр действий.Runs first, because it's a Resource Filter and AddHeader is an Action Filter.
  • Замыкает оставшуюся часть конвейера.Short-circuits the rest of the pipeline.

Поэтому фильтр AddHeader никогда не выполняется для действия SomeResource.Therefore the AddHeader filter never runs for the SomeResource action. Поведение будет аналогичным при применении обоих фильтров на уровне метода действия при условии, что фильтр ShortCircuitingResourceFilter выполняется первым.This behavior would be the same if both filters were applied at the action method level, provided the ShortCircuitingResourceFilter ran first. Фильтр ShortCircuitingResourceFilter выполняется в первую очередь из-за его типа или в связи с явным указанием свойства Order.The ShortCircuitingResourceFilter runs first because of its filter type, or by explicit use of Order property.

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

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

Внедрение зависимостейDependency injection

Фильтры можно добавлять по типу или экземпляру.Filters can be added by type or by instance. Добавляемый экземпляр используется для каждого запроса.If an instance is added, that instance is used for every request. Если добавляется тип, он является активированным.If a type is added, it's type-activated. Активация фильтра означает, что:A type-activated filter means:

  • Экземпляр создается для каждого запроса.An instance is created for each request.
  • Зависимости конструктора заполняются путем внедрения зависимостей.Any constructor dependencies are populated by dependency injection (DI).

Фильтры, которые реализуются как атрибуты и добавляются непосредственно в классы контроллеров или методы действий, не могут иметь зависимости конструктора, предоставленные посредством внедрения зависимостей.Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). Зависимости конструктора не могут предоставляться путем внедрения зависимостей, так как:Constructor dependencies cannot be provided by DI because:

  • Параметры конструктора должны предоставляться для атрибутов в месте их применения.Attributes must have their constructor parameters supplied where they're applied.
  • Это ограничение, налагаемое на использование атрибутов.This is a limitation of how attributes work.

Следующие фильтры поддерживают зависимости конструктора, предоставленные путем внедрения зависимостей:The following filters support constructor dependencies provided from DI:

Предыдущие фильтры могут применяться к контроллеру или методу действия.The preceding filters can be applied to a controller or action method:

Средства ведения журнала доступны путем внедрения зависимостей.Loggers are available from DI. Но следует избегать создания и использования фильтров исключительно для целей ведения журнала.However, avoid creating and using filters purely for logging purposes. Встроенное средство ведения журнала обычно предоставляет все необходимое для ведения журнала.The built-in framework logging typically provides what's needed for logging. При добавлении ведения журналов в фильтры следует учитывать следующее:Logging added to filters:

  • Следует сосредоточиться на бизнес-среде или поведении в привязке к фильтру.Should focus on business domain concerns or behavior specific to the filter.
  • Не следует регистрировать действия или другие события платформы,Should not log actions or other framework events. так как это делают встроенные фильтры.The built in filters log actions and framework events.

ServiceFilterAttributeServiceFilterAttribute

Типы реализации фильтра службы регистрируются в ConfigureServices.Service filter implementation types are registered in ConfigureServices. Атрибут ServiceFilterAttribute извлекает экземпляр фильтра из внедрения зависимостей.A ServiceFilterAttribute retrieves an instance of the filter from DI.

В следующем коде используется AddHeaderResultServiceFilter:The following code shows the AddHeaderResultServiceFilter:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
    }
}

В следующем коде в контейнер внедрения зависимостей добавляется AddHeaderResultServiceFilter:In the following code, AddHeaderResultServiceFilter is added to the DI container:

public void ConfigureServices(IServiceCollection services)
{
    // Add service filters.
    services.AddScoped<AddHeaderResultServiceFilter>();
    services.AddScoped<SampleActionFilterAttribute>();

    services.AddMvc(options =>
    {
        options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
            "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

В следующем коде атрибут ServiceFilter извлекает экземпляр фильтра AddHeaderResultServiceFilter из внедрения зависимостей:In the following code, the ServiceFilter attribute retrieves an instance of the AddHeaderResultServiceFilter filter from DI:

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

При использовании ServiceFilterAttribute задание ServiceFilterAttribute.IsReusable:When using ServiceFilterAttribute, setting ServiceFilterAttribute.IsReusable:

  • Указывает, что экземпляр фильтра можно многократно использовать за пределами области запроса, в которой он был создан.Provides a hint that the filter instance may be reused outside of the request scope it was created within. Среда выполнения ASP.NET Core не гарантирует:The ASP.NET Core runtime doesn't guarantee:

    • что будет создан хотя бы один экземпляр фильтра;That a single instance of the filter will be created.
    • что фильтр не будет повторно запрошен из контейнера внедрения зависимостей на более позднем этапе.The filter will not be re-requested from the DI container at some later point.
  • Не следует использовать с фильтром, который зависит от служб со временем существования, кроме элементов singleton.Should not be used with a filter that depends on services with a lifetime other than singleton.

Объект ServiceFilterAttribute реализует интерфейс IFilterFactory.ServiceFilterAttribute implements IFilterFactory. IFilterFactory предоставляет метод CreateInstance для создания экземпляра IFilterMetadata.IFilterFactory exposes the CreateInstance method for creating an IFilterMetadata instance. CreateInstance загружает указанный тип из внедрения зависимостей.CreateInstance loads the specified type from DI.

TypeFilterAttributeTypeFilterAttribute

Атрибут TypeFilterAttribute похож на ServiceFilterAttribute, но его тип не разрешается напрямую из контейнера внедрения зависимостей.TypeFilterAttribute is similar to ServiceFilterAttribute, but its type isn't resolved directly from the DI container. Он создает экземпляр типа с помощью Microsoft.Extensions.DependencyInjection.ObjectFactory.It instantiates the type by using Microsoft.Extensions.DependencyInjection.ObjectFactory.

Так как типы TypeFilterAttribute не разрешаются напрямую из контейнера внедрения зависимостей:Because TypeFilterAttribute types aren't resolved directly from the DI container:

  • Типы, на которые ссылаются с помощью TypeFilterAttribute, не нужно регистрировать в контейнере внедрения зависимостей.Types that are referenced using the TypeFilterAttribute don't need to be registered with the DI container. Их зависимости выполняются самим контейнером.They do have their dependencies fulfilled by the DI container.
  • Атрибут TypeFilterAttribute может также принимать аргументы конструктора для типа.TypeFilterAttribute can optionally accept constructor arguments for the type.

При использовании TypeFilterAttribute задание TypeFilterAttribute.IsReusable:When using TypeFilterAttribute, setting TypeFilterAttribute.IsReusable:

  • Указывает, что экземпляр фильтра можно многократно использовать за пределами области запроса, в которой он был создан.Provides hint that the filter instance may be reused outside of the request scope it was created within. Среда выполнения ASP.NET Core не предоставляет никаких гарантий, что будет создан хотя бы один экземпляр фильтра.The ASP.NET Core runtime provides no guarantees that a single instance of the filter will be created.

  • Не следует использовать с фильтром, который зависит от служб со временем существования, кроме элементов singleton.Should not be used with a filter that depends on services with a lifetime other than singleton.

В следующем примере показано, как передавать аргументы в тип с помощью TypeFilterAttribute:The following example shows how to pass arguments to a type using TypeFilterAttribute:

[TypeFilter(typeof(LogConstantFilter),
    Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}
public class LogConstantFilter : IActionFilter
{
    private readonly string _value;
    private readonly ILogger<LogConstantFilter> _logger;

    public LogConstantFilter(string value, ILogger<LogConstantFilter> logger)
    {
        _logger = logger;
        _value = value;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        _logger.LogInformation(_value);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    { }
}

Фильтры авторизацииAuthorization filters

Фильтры авторизации:Authorization filters:

  • Выполняются в первую очередь в конвейере фильтров.Are the first filters run in the filter pipeline.
  • Контролируют доступ к методам действий.Control access to action methods.
  • Имеют предшествующий, но не последующий метод.Have a before method, but no after method.

Для работы пользовательских фильтров авторизации требуется настраиваемая платформа авторизации.Custom authorization filters require a custom authorization framework. Настройка политик авторизации или определение пользовательской политики авторизации предпочтительнее создания пользовательского фильтра.Prefer configuring the authorization policies or writing a custom authorization policy over writing a custom filter. Встроенный фильтр авторизации:The built-in authorization filter:

  • Вызывает систему авторизации.Calls the authorization system.
  • Не выполняет авторизацию запросов.Does not authorize requests.

Не вызывайте исключения в фильтрах авторизации:Do not throw exceptions within authorization filters:

  • Исключение не будет обработано.The exception will not be handled.
  • Фильтры исключений не будут обрабатывать исключение.Exception filters will not handle the exception.

При возникновении исключения в фильтре авторизации попробуйте создать запрос.Consider issuing a challenge when an exception occurs in an authorization filter.

Дополнительные сведения об авторизации.Learn more about Authorization.

Фильтры ресурсовResource filters

Фильтры ресурсов:Resource filters:

Фильтры ресурсов полезны для сокращенного выполнения большей части конвейера.Resource filters are useful to short-circuit most of the pipeline. Например, фильтр кэширования предотвращает выполнение остальной части конвейера при попадании в кэше.For example, a caching filter can avoid the rest of the pipeline on a cache hit.

Примеры фильтров ресурсов:Resource filter examples:

Фильтры действийAction filters

Важно!

Фильтры действий не применяются к Razor страницам.Action filters do not apply to Razor Pages. Razor Страницы поддерживают IPageFilter и IAsyncPageFilter .Razor Pages supports IPageFilter and IAsyncPageFilter . Дополнительные сведения см. в разделе Методы фильтрации для Razor Pages.For more information, see Filter methods for Razor Pages.

Фильтры действий:Action filters:

  • Реализуют либо интерфейс IActionFilter, либо интерфейс IAsyncActionFilter.Implement either the IActionFilter or IAsyncActionFilter interface.
  • Их выполнение охватывает выполнение методов действия.Their execution surrounds the execution of action methods.

В следующем фрагменте кода показан пример фильтра действий:The following code shows a sample action filter:

public class MySampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

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

ActionExecutingContext предоставляет следующие свойства:The ActionExecutingContext provides the following properties:

  • ActionArguments позволяет считать входные данные в метод действия.ActionArguments - enables the inputs to an action method be read.
  • Controller позволяет управлять экземпляром контроллера.Controller - enables manipulating the controller instance.
  • Result позволяет при задании свойства Result сократить выполнение метода действия и последующих фильтров действий.Result - setting Result short-circuits execution of the action method and subsequent action filters.

Вызов исключения в методе действия:Throwing an exception in an action method:

  • Предотвращает выполнение последующих фильтров.Prevents running of subsequent filters.
  • В отличие от параметра Result рассматривается как сбой, а не успешный результат.Unlike setting Result, is treated as a failure instead of a successful result.

ActionExecutedContext предоставляет Controller и Result, а также следующие свойства:The ActionExecutedContext provides Controller and Result plus the following properties:

  • Canceled будет иметь значение true, если выполнение действия было сокращено другим фильтром.Canceled - True if the action execution was short-circuited by another filter.

  • Exception будет иметь ненулевое значение, если предыдущее выполнение фильтра действий вызвало исключение.Exception - Non-null if the action or a previously run action filter threw an exception. При присвоении этому свойству ненулевого значения:Setting this property to null:

    • Будет эффективно обрабатываться исключение.Effectively handles the exception.
    • Result будет выполняться так, как при возвращении методом действия.Result is executed as if it was returned from the action method.

Для IAsyncActionFilter вызов ActionExecutionDelegate:For an IAsyncActionFilter, a call to the ActionExecutionDelegate:

  • Приводит к выполнению последующих фильтров действий и метода действия.Executes any subsequent action filters and the action method.
  • Возвращает ActionExecutedContext.Returns ActionExecutedContext.

Для сокращенного выполнения присвойте Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result экземпляру результата и не вызывайте next (ActionExecutionDelegate).To short-circuit, assign Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result to a result instance and don't call next (the ActionExecutionDelegate).

Платформа предоставляет абстрактный класс ActionFilterAttribute, для которого можно создавать подклассы.The framework provides an abstract ActionFilterAttribute that can be subclassed.

Фильтр действий OnActionExecuting можно использовать:The OnActionExecuting action filter can be used to:

  • Для проверки состояния модели.Validate model state.
  • Для получения ошибки, если состояние является недопустимым.Return an error if the state is invalid.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }

Метод OnActionExecuted выполняется после метода действия:The OnActionExecuted method runs after the action method:

  • Он имеет доступ к результатам действия и может управлять ими с помощью свойства Result.And can see and manipulate the results of the action through the Result property.

  • Canceled будет иметь значение true, если выполнение действия было сокращено другим фильтром.Canceled is set to true if the action execution was short-circuited by another filter.

  • Exception будет иметь ненулевое значение, если действие или последующий фильтр действий вызвали исключение.Exception is set to a non-null value if the action or a subsequent action filter threw an exception. Если установить для Exception значение NULL:Setting Exception to null:

    • Будет эффективно обрабатываться исключение.Effectively handles an exception.
    • ActionExecutedContext.Result будет выполняться так, как если бы метод действия вернул его обычным образом.ActionExecutedContext.Result is executed as if it were returned normally from the action method.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }


    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var result = context.Result;
        // Do something with Result.
        if (context.Canceled == true)
        {
            // Action execution was short-circuited by another filter.
        }

        if(context.Exception != null)
        {
            // Exception thrown by action or action filter.
            // Set to null to handle the exception.
            context.Exception = null;
        }
        base.OnActionExecuted(context);
    }
}

Фильтры исключенийException filters

Фильтры исключений:Exception filters:

В следующем примере фильтра исключений используется пользовательское представление ошибок для отображения подробных сведений об исключениях, которые происходят во время разработки приложения:The following sample exception filter uses a custom error view to display details about exceptions that occur when the app is in development:

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly IHostingEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

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

    public void OnException(ExceptionContext context)
    {
        if (!_hostingEnvironment.IsDevelopment())
        {
            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.Result = result;
    }
}

Фильтры исключений:Exception filters:

  • Не имеют предшествующих и последующих событий.Don't have before and after events.
  • Реализуют OnException или OnExceptionAsync.Implement OnException or OnExceptionAsync.
  • Обрабатывать необработанные исключения, происходящие при Razor создании страницы или контроллера, Привязка модели, фильтры действий или методы действий.Handle unhandled exceptions that occur in Razor Page or controller creation, model binding, action filters, or action methods.
  • Не перехватывают исключения, которые возникают в фильтрах ресурсов, фильтрах результатов или при выполнении результата MVC.Do not catch exceptions that occur in resource filters, result filters, or MVC result execution.

Для обработки исключения присвойте свойству ExceptionHandled значение true или напишите ответ.To handle an exception, set the ExceptionHandled property to true or write a response. Это предотвратит распространение исключения.This stops propagation of the exception. Фильтр исключений не может преобразовать исключение в успешное выполнение.An exception filter can't turn an exception into a "success". Это может сделать только фильтр действий.Only an action filter can do that.

Фильтры исключений:Exception filters:

  • Хорошо подходят для перехвата исключений, возникающих в действиях.Are good for trapping exceptions that occur within actions.
  • Не обладает такой гибкостью, как ПО промежуточного слоя обработки ошибок.Are not as flexible as error handling middleware.

ПО промежуточного слоя хорошо подходит для обработки исключений.Prefer middleware for exception handling. Используйте фильтры исключений, только если обработка ошибок осуществляется по-разному с учетом вызванного метода действия.Use exception filters only where error handling differs based on which action method is called. Например, в приложении могут использоваться методы действий как для конечных точек API, так и для представлений или HTML.For example, an app might have action methods for both API endpoints and for views/HTML. Конечные точки API могут возвращать сведения об ошибках в формате JSON, в то время как действия на основе представлений могут возвращать страницу ошибки в формате HTML.The API endpoints could return error information as JSON, while the view-based actions could return an error page as HTML.

Фильтры результатовResult filters

Фильтры результатов:Result filters:

IResultFilter и IAsyncResultFilterIResultFilter and IAsyncResultFilter

В следующем фрагменте кода показан фильтр результатов, который добавляет заголовок HTTP:The following code shows a result filter that adds an HTTP header:

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
    }
}

Тип выполняемого результата зависит от соответствующего действия.The kind of result being executed depends on the action. Действие, возвращающее представление, будет включать всю обработку Razor в рамках выполняемого объекта ViewResult.An action returning a view would include all razor processing as part of the ViewResult being executed. В процессе выполнения результата метод API может производить сериализацию.An API method might perform some serialization as part of the execution of the result. Дополнительные сведения о результатах действий.Learn more about action results.

Фильтры результатов выполняются только в том случае, когда действие или фильтры действий предоставляют результат действия.Result filters are only executed when an action or action filter produces an action result. Фильтры результатов не выполняются в следующих случаях:Result filters are not executed when:

  • Фильтр авторизации или ресурсов выполняет сокращение конвейера.An authorization filter or resource filter short-circuits the pipeline.
  • Фильтр исключений обрабатывает исключение и выдает результат действия.An exception filter handles an exception by producing an action result.

Метод Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting может сокращать выполнение результата действия и последующих фильтров результатов, присваивая свойству Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel значение true.The Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting method can short-circuit execution of the action result and subsequent result filters by setting Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Cancel to true. При сокращении выполнения выполните запись в объект ответа, чтобы избежать формирования пустого ответа.Write to the response object when short-circuiting to avoid generating an empty response. Вызов исключения в IResultFilter.OnResultExecuting:Throwing an exception in IResultFilter.OnResultExecuting will:

  • Предотвращает выполнение результата действия и последующих фильтров.Prevent execution of the action result and subsequent filters.
  • Рассматривается как сбой, а не успешный результат.Be treated as a failure instead of a successful result.

Если запускается метод Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted, ответ, скорее всего, уже был отправлен клиенту.When the Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted method runs, the response has likely already been sent to the client. Если ответ уже был отправлен клиенту его больше нельзя изменить.If the response has already been sent to the client, it cannot be changed further.

ResultExecutedContext.Canceled будет иметь значение true, если выполнение результата действия было сокращено другим фильтром.ResultExecutedContext.Canceled is set to true if the action result execution was short-circuited by another filter.

ResultExecutedContext.Exception будет иметь ненулевое значение, если результат действия или последующий фильтр результатов вызвали исключение.ResultExecutedContext.Exception is set to a non-null value if the action result or a subsequent result filter threw an exception. Присвоение Exception значения NULL приводит к обработке исключения и предотвращает его последующий вызов ASP.NET Core на дальнейших этапах конвейера.Setting Exception to null effectively handles an exception and prevents the exception from being rethrown by ASP.NET Core later in the pipeline. Надежный способ записи данных в ответ при обработке исключения в фильтре результатов отсутствует.There is no reliable way to write data to a response when handling an exception in a result filter. Если результат действия вызывает исключение в процессе выполнения и заголовки уже были переданы в клиент, надежного механизма отправки кода сбоя не существует.If the headers have been flushed to the client when an action result throws an exception, there's no reliable mechanism to send a failure code.

Для IAsyncResultFilter вызов к await next для ResultExecutionDelegate приводит к выполнению последующих фильтров результатов и результата действия.For an IAsyncResultFilter, a call to await next on the ResultExecutionDelegate executes any subsequent result filters and the action result. Для сокращения выполнения задайте ResultExecutingContext.Cancel для true и не вызывайте ResultExecutionDelegate:To short-circuit, set ResultExecutingContext.Cancel to true and don't call the ResultExecutionDelegate:

public class MyAsyncResponseFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                             ResultExecutionDelegate next)
    {
        if (!(context.Result is EmptyResult))
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }

    }
}

Платформа предоставляет абстрактный класс ResultFilterAttribute, для которого можно создавать подклассы.The framework provides an abstract ResultFilterAttribute that can be subclassed. Представленный ранее класс AddHeaderAttribute — это пример атрибута фильтра результатов.The AddHeaderAttribute class shown previously is an example of a result filter attribute.

IAlwaysRunResultFilter и IAsyncAlwaysRunResultFilterIAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter

Интерфейсы IAlwaysRunResultFilter и IAsyncAlwaysRunResultFilter объявляют реализацию IResultFilter, которая выполняется для всех результатов действий.The IAlwaysRunResultFilter and IAsyncAlwaysRunResultFilter interfaces declare an IResultFilter implementation that runs for all action results. Сюда включены результаты действий, созданные:This includes action results produced by:

  • фильтрами авторизации и фильтрами ресурсов, которые сокращают ответ;Authorization filters and resource filters that short-circuit.
  • фильтрами исключений.Exception filters.

Например, следующий фильтр всегда выполняется, задавая результат действия (ObjectResult) с кодом состояния 422 Unprocessable Entity при сбое согласования содержимого:For example, the following filter always runs and sets an action result (ObjectResult) with a 422 Unprocessable Entity status code when content negotiation fails:

public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult &&
            statusCodeResult.StatusCode == 415)
        {
            context.Result = new ObjectResult("Can't process this!")
            {
                StatusCode = 422,
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

IFilterFactoryIFilterFactory

Объект IFilterFactory реализует интерфейс IFilterMetadata.IFilterFactory implements IFilterMetadata. Поэтому экземпляр IFilterFactory можно использовать в качестве экземпляра IFilterMetadata в любом месте конвейера фильтров.Therefore, an IFilterFactory instance can be used as an IFilterMetadata instance anywhere in the filter pipeline. Когда среда выполнения готовится вызвать фильтр, она пытается привести его к IFilterFactory.When the runtime prepares to invoke the filter, it attempts to cast it to an IFilterFactory. Если приведение проходит успешно, вызывается метод CreateInstance для создания вызываемого экземпляра IFilterMetadata.If that cast succeeds, the CreateInstance method is called to create the IFilterMetadata instance that is invoked. Это обеспечивает высокую степень гибкости, так как при запуске приложения нет необходимости в явном и точном определении конвейера фильтров.This provides a flexible design, since the precise filter pipeline doesn't need to be set explicitly when the app starts.

Еще один подход к созданию фильтров заключается в реализации IFilterFactory с помощью настраиваемых атрибутов:IFilterFactory can be implemented using custom attribute implementations as another approach to creating filters:

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[] { "My header" });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Приведенный выше код можно проверить, выполнив скачиваемый пример:The preceding code can be tested by running the download sample:

  • Вызовите средства для разработчика (F12).Invoke the F12 developer tools.
  • Перейдите на страницу https://localhost:5001/Sample/HeaderWithFactory.Navigate to https://localhost:5001/Sample/HeaderWithFactory.

В средствах для разработчика (F12) отобразятся следующие заголовки ответа, добавленные примером кода:The F12 developer tools display the following response headers added by the sample code:

  • Автор:Joe Smithauthor: Joe Smith
  • globaladdheader: Result filter added to MvcOptions.Filtersglobaladdheader: Result filter added to MvcOptions.Filters
  • внутренний:My headerinternal: My header

Приведенный выше код создает заголовок ответа internal: My header.The preceding code creates the internal: My header response header.

Реализация IFilterFactory в атрибутеIFilterFactory implemented on an attribute

Фильтры, реализующие IFilterFactory, используются для фильтров, которые:Filters that implement IFilterFactory are useful for filters that:

  • Не требуют передачи параметров.Don't require passing parameters.
  • Содержат зависимости конструктора, которые должны заполняться путем внедрения зависимостей.Have constructor dependencies that need to be filled by DI.

Объект TypeFilterAttribute реализует интерфейс IFilterFactory.TypeFilterAttribute implements IFilterFactory. IFilterFactory предоставляет метод CreateInstance для создания экземпляра IFilterMetadata.IFilterFactory exposes the CreateInstance method for creating an IFilterMetadata instance. CreateInstance загружает указанный тип из контейнера служб (внедрение зависимостей).CreateInstance loads the specified type from the services container (DI).

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]:The following code shows three approaches to applying the [SampleActionFilter]:

[SampleActionFilter]
public IActionResult FilterTest()
{
    return Content($"From FilterTest");
}

[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
    return Content($"From ServiceFilterTest");
}

// ServiceFilter must be registered in ConfigureServices or
// System.InvalidOperationException: No service for type '<filter>' has been registered.
// Is thrown.
[ServiceFilter(typeof(SampleActionFilterAttribute))]
public IActionResult ServiceFilterTest()
{
    return Content($"From ServiceFilterTest");
}

В приведенном выше коде декорирование метода с использованием [SampleActionFilter] является рекомендуемым способом применения SampleActionFilter.In the preceding code, decorating the method with [SampleActionFilter] is the preferred approach to applying the SampleActionFilter.

Использование ПО промежуточного слоя в конвейере фильтровUsing middleware in the filter pipeline

Фильтры ресурсов по принципу работы похожи на ПО промежуточного слоя тем, что они заключают в себя выполнение всех объектов, находящихся далее в конвейере.Resource filters work like middleware in that they surround the execution of everything that comes later in the pipeline. При этом фильтры отличаются от ПО промежуточного слоя тем, что они являются частью среды выполнения ASP.NET Core, то есть они имеют доступ к контексту и конструкциям ASP.NET Core.But filters differ from middleware in that they're part of the ASP.NET Core runtime, which means that they have access to ASP.NET Core context and constructs.

Чтобы использовать ПО промежуточного слоя в качестве фильтра, создайте тип с методом Configure, определяющим ПО промежуточного слоя, которое нужно внедрить в конвейер фильтров.To use middleware as a filter, create a type with a Configure method that specifies the middleware to inject into the filter pipeline. В примере ниже ПО промежуточного слоя локализации применяется для определения текущих языка и региональных параметров для запроса:The following example uses the localization middleware to establish the current culture for a request:

public class LocalizationPipeline
{
    public void Configure(IApplicationBuilder applicationBuilder)
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fr")
        };

        var options = new RequestLocalizationOptions
        {

            DefaultRequestCulture = new RequestCulture(culture: "en-US", 
                                                     uiCulture: "en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        };
        options.RequestCultureProviders = new[] 
            { new RouteDataRequestCultureProvider() { Options = options } };

        applicationBuilder.UseRequestLocalization(options);
    }
}

Используйте MiddlewareFilterAttribute для выполнения ПО промежуточного слоя:Use the MiddlewareFilterAttribute to run the middleware:

[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
    return Content($"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
        + $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}

Фильтры ПО промежуточного слоя выполняются на том же этапе конвейера фильтров, что и фильтры ресурсов, перед привязкой модели и после остальной части конвейера.Middleware filters run at the same stage of the filter pipeline as Resource filters, before model binding and after the rest of the pipeline.

Дальнейшие действияNext actions