Фильтры действий выполняются после фильтров авторизации и фильтров ресурсов , а также, после привязки модели. Для создания такого фильтра нам необходимо реализовать один из интерфейсов — IActionFilter или IActionFilterAsync. Такие фильтры удобно использовать для анализа привязки модели, для сокращения исходного кода контроллера, а также для модификации входных данных методов контроллера.
Пример фильтра действий в ASP.NET Core
Рассмотрим следующий код фильтра действий:
public class PageTitleFilter : IAsyncActionFilter
{
readonly IHttpClientFactory _factory;
public PageTitleFilter(IHttpClientFactory factory)
{
_factory = factory;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
string url;
string title = "Заголовок не обнаружен";
if (context.ActionArguments.TryGetValue("url", out var urlArg) || (urlArg != null))
{
url = (string)urlArg;
var client = _factory.CreateClient();
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode)
{
string content = await result.Content.ReadAsStringAsync();
string regex = @"(?<=<title.*>)([\s\S]*)(?=</title>)"; //ищем Title
Regex ex = new(regex, RegexOptions.IgnoreCase);
var match = ex.Match(content);
if (match.Success)
{
title = match.Value.Trim(); //значение Title
}
}
}
context.HttpContext.Items.Add("title", title);
await next.Invoke();
}
}
Первое, на что необходимо обратить внимание в этом фильтре — это конструктор, в который передается зависимость IHttpClientFactory. Это значит, во первых, что нам необходимо добавить в список сервисов, сервис для использования HttpClient, а, во-вторых, нам необходимо учитывать то, что фильтр использует зависимости и использовать его необходимо через TypedFilterAttribute или через ServiceFilterAttribute, как мы это делали в этой части.
Теперь рассмотрим основной метод фильтра — OnActionExecutionAsync(). У этого метода два параметра — контекст действия (ActionExecutingContext context) и делегат следующего фильтра (ActionExecutionDelegate next). Контекст действия мы используем для того, чтобы получить параметры запроса, а именно — параметр с именем url:
if (context.ActionArguments.TryGetValue("url", out var urlArg) || (urlArg != null))
Делегат же мы используем в конце метода, чтобы выполнить следующий фильтр в очереди:
await next.Invoke();
Если параметр url получен, то мы создаем новый экземпляр HttpClient:
var client = _factory.CreateClient();
скачиваем страничку по указанному url и, используя регулярные выражения получаем заголовок страницы (мета-тэг title):
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode)
{
string content = await result.Content.ReadAsStringAsync();
string regex = @"(?<=<title.*>)([\s\S]*)(?=</title>)"; //ищем Title
Regex ex = new(regex, RegexOptions.IgnoreCase);
var match = ex.Match(content);
if (match.Success)
{
title = match.Value.Trim(); //значение Title
}
}
Далее, если заголовок получен, то его значение записывается в контекст запроса:
context.HttpContext.Items.Add("title", title);
или, если строка заголовка не найдена, то в Items будет записана строка «Заголовок не обнаружен». Теперь применим этот фильтр действий в приложении.
Применение фильтра действий
Вначале зарегистрируем необходимые сервисы в методе Main():
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddHttpClient();
builder.Services.AddTransient<PageTitleFilter>();
//здесь стандартный код метода Main()
}
Здесь мы добавили в список сервисов два сервиса — для использования клиента Http:
builder.Services.AddHttpClient();
и наш фильтр
builder.Services.AddTransient<PageTitleFilter>();
Теперь изменим код представление Views/Home/Index.cshtml следующим образом:
<h3>@ViewData["url"]</h3> <h3>@ViewData["title"]</h3>
здесь мы пользуемся ViewData, чтобы передать данные из контроллера в представление. Осталось дописать код контроллера следующим образом:
[ServiceFilter(typeof(PageTitleFilter))]
public IActionResult Index(string? url)
{
ViewData["url"] = url;
ViewData["title"] = (string)HttpContext.Items["title"];
return View();
}
Для метода Index() мы указали необязательный параметр url и применили к нему фильтр действий:
[ServiceFilter(typeof(PageTitleFilter))]
В самом методе мы передаем в представление необходимые данные. Теперь можно запустить приложение и посмотреть на результат:
Как только в параметрах запроса передается какой-либо url, фильтр пытается считать заголовок страницы, а в представлении мы пытаемся этот заголовок вывести на главной странице нашего приложения.
Итого
Фильтры действий реализуют интерфейс IActionFilter или IActionFilterAsync и могут использоваться как для анализа привязки модели так и, например, для модификации входных параметров запроса.

