Фильтры действий выполняются после фильтров авторизации и фильтров ресурсов , а также, после привязки модели. Для создания такого фильтра нам необходимо реализовать один из интерфейсов — 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
и могут использоваться как для анализа привязки модели так и, например, для модификации входных параметров запроса.