Введение

ASP.NET Core — это мощный и универсальный фреймворк для создания веб-приложений. Он предоставляет широкий набор возможностей для создания надежных и масштабируемых приложений, и одной из ключевых фич, расширяющих его функциональность, являются фильтры действий (Action Filters). Фильтры действий позволяют выполнять код до или после выполнения метода контроллера (или метода действия), что дает возможность добавить в приложение сквозной функционал.

В этой статье мы рассмотрим фильтры действий в ASP.NET Core, разберемся в их типах и научимся создавать пользовательские фильтры для расширения функциональности нашего веб-приложения.

Что из себя представляют фильтры действий?

Фильтры действий в ASP.NET Core — это атрибуты, которые могут быть применены к методам действий контроллера (controller action methods) для выполнения предварительной или последующей обработки для этих методов. Они позволяют добавлять функционал, который будет выполняется до или после выполнения методов контроллера. Фильтры действий могут использоваться для реализации таких сквозных задач, как протоколирование, аутентификация, кэширование и т.д.

В ASP.NET Core существует целых пять типов фильтров действий:

  1. Фильтры авторизации (Authorization Filter)

  2. Фильтры ресурсов (Resource Filter)

  3. Фильтры действий (Action Filter)

  4. Фильтры результатов (Result Filter)

  5. Фильтры исключений (Exception Filter)

Action Filter
Action Filter

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

Фильтры авторизации играют важную роль в обеспечении соблюдения правил аутентификации и авторизации в веб-приложении. Фильтры авторизации обычно используются для реализации предоставления доступа к определенным частям приложения только аутентифицированным и авторизованным пользователям. Эти фильтры выполняются перед методами контроллера, позволяя проверить учетные данные и разрешения пользователя и, соответственно, предоставить или запретить доступ. Они являются одним из основных компонентов для реализации безопасности и контроля доступа в приложениях ASP.NET Core.

Пример:

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

Сначала определим пользовательский фильтр авторизации:

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

public class CustomAuthorizationFilter : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // Проверяем принадлежность пользователя к роли "Admin"
        if (!context.HttpContext.User.IsInRole("Admin"))
        {
            // Если у него нет этой роли, то отказать в доступе и вернуть статус forbidden
            context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
        }
    }
}

 Затем применим CustomAuthorizationFilter к методу контроллера:

[CustomAuthorizationFilter] // Применяем наш фильтр авторизации
public IActionResult AdminOnlyAction()
{
    // Это действие доступно только пользователям с ролью "Admin".
    return View();
}

В данном примере фильтр CustomAuthorizationFilter применяется к методу AdminOnlyAction. Когда пользователь пытается получить доступ к этому действию, фильтр проверяет, принадлежит ли ему роль "Admin", используя метод IsInRole. Если пользователю не принадлежит роль "Admin", фильтр устанавливает код состояния HTTP-ответа 403 Forbidden, запрещая доступ к действию.

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

Фильтры ресурсов в ASP.NET Core — это тип фильтров действий, который позволяет выполнять действия, влияющие на весь HTTP-запрос и ответ, например, изменять заголовки ответа или обрабатывать глобальные исключения. Эти фильтры выполняются до того, как другие типы фильтров (фильтры авторизации, действий, результатов и исключений) получают доступ к HTTP-контексту, и могут влиять на весь конвейер обработки запроса.

  • Глобальная обработка исключений: Фильтры ресурсов могут использоваться для обработки исключений, возникающих в процессе обработки запросов. Реализовав фильтр ресурсов для обработки исключений, можно перехватывать и обрабатывать исключения глобально, обеспечивая согласованный способ регистрации ошибок, отображения детализированных страниц ошибок или выполнения других действий.

  • Модификация ответа: С помощью фильтров ресурсов можно модифицировать объект ответа, например, добавить пользовательские заголовки в ответ, изменить код состояния или содержимое ответа.

  • Предварительная обработка запроса: Фильтры ресурсов могут выполнять действия в начале конвейера запроса, например, устанавливать переменные запроса, или выполнять другие задачи предварительной обработки.

  • Глобальное логирование: Вы можете использовать фильтры ресурсов для глобального логирования, чтобы регистрировать информацию о входящих запросах, времени отклика или других показателях, относящихся ко всему приложению.

Пример

Давайте создадим простой фильтр ресурсов, который будет добавлять пользовательский заголовок ответа к каждому ответу в нашем ASP.NET Core приложении. В этом примере мы добавим к ответу заголовок "X-Custom-Header".

Создадим пользовательский фильтр ресурсов:

using Microsoft.AspNetCore.Mvc.Filters;

public class AddCustomHeaderResourceFilter : IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        // Код, выполняемый перед действием
        context.HttpContext.Response.Headers.Add("X-Custom-Header", "MyCustomValue");
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
        // Код, выполняемый после действия
    }
}

  Его можно использовать в методе ConfigureServices:

services.AddMvc(options =>
{
    options.Filters.Add<AddCustomHeaderResourceFilter>();
});

Теперь этот фильтр ресурсов будет выполняться для каждого запроса в вашем ASP.NET Core приложении, добавляя в заголовки ответа "X-Custom-Header".

Фильтры ресурсов — это мощный способ выполнения глобальных действий, которые применяются ко всем запросам и ответам в приложении, что делает их ценным инструментом для решения таких задач, как глобальная обработка исключений, модификация ответов и предварительная обработка запросов.

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

Фильтры действий в ASP.NET Core — это атрибуты, позволяющие добавлять логику, которая работает до и после выполнения отдельных методов действий в контроллерах. Эти фильтры используются для выполнения таких задач, как логирование, проверка ввода, изменение результата действия и т.д.

Пример:

Создадим простой фильтр фильтр действий для регистрации начала и окончания выполнения метода контроллера.

Создадим пользовательский фильтр действий:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

public class LogActionFilter : IActionFilter
{
    private readonly ILogger<LogActionFilter> _logger;

    public LogActionFilter(ILogger<LogActionFilter> logger)
    {
        _logger = logger;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        //  Этот метод выполняется до метода контроллера
        _logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' is starting.");
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Этот метод выполняется после метода контроллера
        _logger.LogInformation($"Action '{context.ActionDescriptor.DisplayName}' has completed.");
    }
}

Его можно использовать в методе ConfigureServices.

services.AddMvc(options =>
{
    options.Filters.Add<LogActionFilter>();
});

Применим наш фильтр действий к методу действия контроллера.

[ServiceFilter(typeof(LogActionFilter))] // Применяем фильтр к данному методу контроллера
public IActionResult MyAction()
{
    // Логика нашего метода контроллера
}

Теперь при каждом вызове метода MyAction фильтр LogActionFilter будет регистрировать начало и конец действия, представляя собой простой способ контроля за выполнением методов контроллера.

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

Фильтры результатов в ASP.NET Core — это тип фильтров действий, который выполняет код после выполнения метода действия, но до обработки результата и отправки его клиенту. Такие фильтры полезны для модификации ответа или результата, добавления пользовательских заголовков или выполнения действий, связанных с ответом, до его возврата клиенту.

Пример:

Давайте создадим простой фильтр результатов для добавления в ответ пользовательского заголовка.

Создадим пользовательский фильтр результатов:

using Microsoft.AspNetCore.Mvc.Filters;

public class AddCustomHeaderResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Этот метод запускается до выполнения результата
        context.HttpContext.Response.Headers.Add("X-Custom-Header", "MyCustomValue");
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Этот метод запускается после выполнения результата
    }
}

Это можно сделать в методе ConfigureServices:

services.AddMvc(options =>
{
    options.Filters.Add<AddCustomHeaderResultFilter>();
});

Применим наш фильтр результатов к методу действия контроллера.

[ServiceFilter(typeof(AddCustomHeaderResultFilter))] // Применяем фильтр к данному методу контроллера
public IActionResult MyAction()
{
    // Логика нашего метода контроллера
}

При вызове метода MyAction фильтр AddCustomHeaderResultFilter будет добавлять "X-Custom-Header" в заголовки ответа перед его отправкой клиенту. Это может быть полезно в сценариях, где требуется добавить пользовательские заголовки в ответ, установить типы содержимого ответа или выполнить другие действия, связанные с ответом.

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

Фильтры исключений в ASP.NET Core — это разновидность фильтров действий, специально разработанных для обработки исключений, возникающих во время выполнения метода контроллера. Эти фильтры позволяют определять пользовательскую логику для изящной обработки и реагирования на исключения, обеспечивая централизованную обработку ошибок, в целом улучшая юзер экспириенс.

Как работают фильтры исключений?

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

Примеры использования фильтров исключений

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

  • Логирование и отчетность: С помощью фильтров исключений можно регистрировать исключения, что облегчает выявление и устранение проблем. Кроме того, можно передавать отчеты об исключениях во внешние системы для дальнейшего анализа.

  • Постепенное отключение функций: В случаях, когда возникает исключение, но приложение продолжает функционировать, фильтр исключений может обработать ошибку и вернуть частичный или ухудшенный ответ вместо полного отказа.

Пример:

Создадим простой фильтр исключений для регистрации и обработки исключений.

Создадим пользовательский фильтр исключений:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly ILogger<CustomExceptionFilter> _logger;

    public CustomExceptionFilter(ILogger<CustomExceptionFilter> logger)
    {
        _logger = logger;
    }

    public void OnException(ExceptionContext context)
    {
        // Регистрируем исключение
        _logger.LogError($"An exception occurred: {context.Exception.Message}");

        // Обрабатываем исключение
        context.Result = new ViewResult { ViewName = "Error" };
        context.ExceptionHandled = true;
    }
}

Это можно сделать в методе ConfigureServices:

services.AddMvc(options =>
{
    options.Filters.Add<CustomExceptionFilter>();
});

Применим фильтр исключений к методу действия контроллера:

[ServiceFilter(typeof(CustomExceptionFilter))] // Применяем фильтр к данному методу контроллера
public IActionResult MyAction()
{
    // Логика нашего метода контроллера
}

Если в методе MyAction возникает необработанное исключение, то CustomExceptionFilter регистрирует ошибку и перенаправляет пользователя на пользовательскую страницу, подготовленную для этой ошибки.

Реальные примеры использования

Фильтры действий невероятно универсальны и могут находить применение в самых разных сценариях. Вот несколько реальных примеров использования фильтров действий в ASP.NET Core.

  • Логирование: Вы можете создать фильтр действий, который будет записывать в логи информацию о выполнении методов контроллеров, что поможет при отладке и мониторинге.

  • Валидация: Реализуйте проверку достоверности входных данных перед выполнением метода контроллера, чтобы убедиться в том, что входные данные являются достоверными.

  • Кэширование: Используйте фильтры действий для кэширования результатов методов контроллера, чтобы повысить производительность и сократить количество обращений к базе данных или API.

  • Безопасность: Реализация проверок безопасности и логики авторизации с использованием фильтров действий для ограничения доступа к определенным методам контроллера на основе ролей и разрешений пользователей.

  • Обработка исключений: Создание пользовательских фильтров исключений для последовательной и удобной обработки и регистрации исключений.

Заключение

Фильтры действий в ASP.NET Core — это мощный инструмент для добавления сквозных задач и расширения функциональности веб-приложений. Понимание различных типов фильтров действий и создание пользовательских фильтров при необходимости позволяет оптимизировать такие распространенные задачи, как логирование, валидация, кэширование и обеспечение безопасности, что делает приложение более надежным и удобным в обслуживании. Независимо от того, создаете ли вы небольшое веб-приложение или крупную корпоративную систему, фильтры действий помогут вам добиться лучшей организации кода и его удобства в обслуживании, придерживаясь принципа DRY (Don't Repeat Yourself).

Спасибо за внимание!


Часто задаваемые вопросы:

Вопрос: В чем разница между методами GET и POST в ASP.NET Core?

Ответ: В ASP.NET Core GET и POST — это HTTP-методы, используемые для запроса и отправки данных соответственно. GET используется для получения данных и не должен оказывать влияния на сервер. Обычно с его помощью извлекаются данные и отображаются представления. POST, с другой стороны, используется для отправки данных на сервер, часто для создания, обновления или удаления ресурсов. POST-запросы могут изменять данные на сервере, поэтому их следует использовать при отправке форм или внесении изменений.

Вопрос: Как передать данные из метода контроллера в представление в ASP.NET Core?

Ответ: Передать данные из метода контроллер в представление в ASP.NET Core можно с помощью ViewBag, ViewData или сильно типизированной модели. ViewBag и ViewData — это динамические объекты, позволяющие хранить и извлекать данные в рамках контроллера и представления. Сильно типизированная модель, как правило, ViewModel, является более структурированным подходом и рекомендуется для сложных сценариев, поскольку обеспечивает типобезопасность и в целом лучшую организацию кода.

Вопрос: Что такое параметры действия и как они связаны с методами контроллера в ASP.NET Core?

Ответ: Параметры действия (action parameters) в ASP.NET Core представляют собой значения, передаваемые методу контроллера. Эти значения могут поступать из различных источников, таких как значения маршрута, строки запроса, данные формы или тело запроса. Для автоматического сопоставления этих значений с параметрами метода контроллера в ASP.NET Core используется процесс, называемый привязкой модели (model binding). Для указания источника данных для каждого параметра можно использовать такие атрибуты, как [FromRoute], [FromQuery] и [FromBody]. Привязка модели упрощает процесс извлечения данных из HTTP-запроса и делает их доступными для обработки в методах контроллера.

Вопрос: В чем разница между ActionResult и IActionResult в методах контроллера в ASP.NET Core?

Ответ: ActionResult и IActionResult используются для возврата результатов из методов контроллера в ASP.NET Core. Основное различие заключается в том, что ActionResult — это подтип IActionResult. ActionResult включает в себя несколько производных классов, таких как ViewResult, JsonResult и RedirectToActionResult, которые обеспечивают специфическое поведение для ряда распространенных сценариев. IActionResult, с другой стороны, является более общим классом и может использоваться для возврата результатов любого типа. Выбор между ActionResult и IActionResult зависит от того, какой уровень конкретики и безопасности вы требуете от результатов метода контроллера.

Вопрос: Как обрабатывать асинхронные действия в методах контроллеров в ASP.NET Core?

Ответ: Для обработки асинхронных действий в ASP.NET Core можно использовать ключевые слова async и await. Можно пометить метод контроллера модификатором async и вернуть Task<IActionResult>, что позволит выполнять асинхронные операции, например запросы к базе данных или вызовы API, не блокируя поток запроса. Это гарантирует, что приложение останется отзывчивым и сможет эффективно обрабатывать одновременные запросы. Асинхронные действия могут быть полезны для повышения масштабируемости и производительности веб-приложения, особенно при работе с операциями, связанными с вводом-выводом.

В заключение приглашаем всех желающих на бесплатный открытый урок «Пишем свой API: OData», который пройдет сегодня в 20:00. На нем мы получим представление и примеры работы с протоколом Odata, а также поговорим о его преимущствах по сравнению с REST на ASP.NET Core. Записаться можно на странице онлайн-курса «C# ASP.NET Core разработчик».

Комментарии (1)


  1. Oceanshiver
    15.11.2023 11:04
    -1

    OTUS. OTUS никогда не меняется