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

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

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии