ASP.NET Core | Фильтры действий

Asp.net core | фильтры действий

Последнее обновление: 11.12.2022

Фильтры действий (Action Filters) реализуют либо интерфейс IActionFilter, либо интерфейс IAsyncActionFilter.
Фильтры действий выполняются после фильтров авторизации и ресурсов и уже после того, как произошла привязка модели.
Поэтому данные фильтры являются прекрасным местом для исследования результатов привязки модели, а также модификации входных данных в метод контроллера или его результата.

К примеру, добавим в проект следующий фильтр:

using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace FiltersApp.Filters
{
    public class WhitespaceAttribute : Attribute, IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {

        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            var response = context.HttpContext.Response;
            // Если sitemap, то ничего не делаем
            if (context.HttpContext.Request.Path.ToString() == "/sitemap.xml") return;
            if (response.Body == null) return;
            response.Body = new SpaceCleaner(response.Body);
        }

        // вспомогательный класс для удаления пробелов
        private class SpaceCleaner : Stream
        {
            private readonly Stream outputStream;
            StringBuilder _s = new StringBuilder();

            public SpaceCleaner(Stream filterStream)
            {
                if (filterStream == null)
                    throw new ArgumentNullException("filterStream is not determined");
                outputStream = filterStream;
            }

            public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            {
                var html = Encoding.UTF8.GetString(buffer, offset, count);
                //регулярное выражение для поиска пробелов между тегами
                var reg = new Regex(@"(?<=s)s (?![^<>]*</pre>)");
                html = reg.Replace(html, string.Empty);
                buffer = Encoding.UTF8.GetBytes(html);
                await outputStream.WriteAsync(buffer, 0, buffer.Length, cancellationToken);
            }

            public override void Write(byte[] buffer, int offset, int count)
            {
                throw new NotSupportedException();
            }
            // нереализованные методы Stream
            public override int Read(byte[] buffer, int offset, int count)
            {
                throw new NotSupportedException();
            }
            public override bool CanRead { get { return false; } }
            public override bool CanSeek { get { return false; } }
            public override bool CanWrite { get { return true; } }
            public override long Length { get { throw new NotSupportedException(); } }
            public override long Position
            {
                get { throw new NotSupportedException(); }
                set { throw new NotSupportedException(); }
            }
            public override void Flush()
            {
                outputStream.Flush();
            }
            public override long Seek(long offset, SeekOrigin origin)
            {
                throw new NotSupportedException();
            }
            public override void SetLength(long value)
            {
                throw new NotSupportedException();
            }
        }
    }
}

В данном случае у нас определен фильтр пробелов, который удаляет пробелы из выходного кода html. Основная работа перенесена во вспомогательный класс
SpaceCleaner. А ключевыми здесь являются методы OnActionExecuting() и OnActionExecuted().

Метод OnActionExecuting() вызывается до выполнения метода контроллера. В качестве параметра он принимает контекст выполнения – объект
ActionExecutingContext, который имеет ряд свойств. Посредством изменения значения
ActionExecutingContext.ActionArguments можно манипулировать параметрами метода.
Либо через значение ActionExecutingContext.Controller можно управлять контроллером.

Похожее:  НПФ Будущее — личный кабинет: вход / официальный сайт

Кроме того, метод OnActionExecuting() может завершить обработку запроса путем установки свойства ActionExecutingContext.Result.

Метод OnActionExecuted() вызывается после выполнения метода и получает в качестве параметра объект ActionExecutedContext. На этом этапе мы можем увидеть результат выполнения и как-то
его изменить через свойство ActionExecutedContext.Result.

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

Применение фильтра:

public class HomeController : Controller
{
    [Whitespace]
    public IActionResult Index()
    {
        return View();
    }
}

При реализации интерфейса IAsyncActionFilter необходимо определить его метод OnActionExecutionAsync(), который объединяет возможности
методов OnActionExecuting() и OnActionExecuted(). А вызов await next() для передаваемого в качестве параметра
объекта ActionExecutionDelegate позволит выполнить последующие фильтры действий.

К примеру, добавим новый фильтр, который назовем CheckFilterAttribute:

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

namespace FiltersApp.Filters
{
    public class CheckFilterAttribute : Attribute, IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, 
														ActionExecutionDelegate next)
        {
            if (context.ModelState.IsValid == false)
                context.ActionArguments["id"] = 34;
            await next();
        }
    }
}

Данный метод применяет интерфейс IAsyncActionFilter, поэтому реализует только один метод OnActionExecutionAsync().
В качестве параметра опять же передается контекст выполнения ActionExecutingContext и делегат ActionExecutionDelegate, через который мы можем вызвать другие фильтры действий.

Суть фильтра довольно проста – для параметра id в качестве значения устанавливается число 34.

Применение фильтра будет аналогично:

public class HomeController : Controller
{
    [CheckFilter]
    public IActionResult Index(int id)
    {
        return Content($"id = {id}");
    }
}

При обращении к методу вне зависимости от того, какое значение будет передано для id, фильтр действий все равно его поменяет на число 34.

Action Filters in ASP.NET Core MVC

НазадСодержаниеВперед

Применение фильтра

Теперь применим выше определенный фильтр и для этого определим следующий код контроллера:

using Microsoft.AspNetCore.Mvc;
using MvcApp.Filters;  // пространство имен фильтра SimpleResourceFilter

namespace MvcApp.Controllers
{
    [SimpleResourceFilter]
    public class HomeController : Controller
    {
        public IActionResult Index() => View();
    }
}

В данном случае фильтр применяется ко всему контроллеру. Запустим проект, и после получения ответа в куках окажутся данные с ключом “LastVisit”:

Применение фильтров в ASP.NET Core MVC и C#

Если бы мы не использовали бы фильтр, то в каждом методе контроллера нам надо было бы определить строку кода:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *