Создание middleware на базе фабрики классов

Компоненты middleware можно создавать, как в форме делегатов (inline middleware), так и в виде отдельных классов. При создании класса middleware мы использовали т.н. активацию middleware по соглашению, когда к создаваемом классу предъявляются определенные требования к конструктору, реализуемым методам и т.д. Вместе с этим в ASP.NET Core, начиная с версии 2.0 появилась третья возможность создания компонентов middleware — на базе фабрики классов (factory-based middleware).

Интерфейс IMiddleware

Интерфейс IMiddleware представляет собой компонент middleware, создаваемый на базе фабрики классов. Этот интерфейс содержит всего один метод:

Task InvokeAsync(HttpContext context, RequestDelegate next);

в котором происходит обработка запроса на основании контекста (context) и, при необходимости, вызывается следующий компонент middleware (next). Посмотрим, в чем отличия между вариантом активации middleware по соглашению и factory-based middleware. Для этого вспомним класс CharsetMiddleware, который мы разрабатывали ранее:

public class CharsetMiddleware
{
    private RequestDelegate _next;
    public CharsetMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var charset = context.Request.Query["charset"];
        context.Response.OnStarting(
                () => {
                    if (string.IsNullOrWhiteSpace(charset))
                        context.Response.ContentType = "text/plain; charset=utf-8";
                    else
                        context.Response.ContentType = $"text/plain; charset={charset}";
                    return Task.CompletedTask;
                });
        await _next(context);
    }
}

Попробуем переписать его, используя интерфейс IMiddleware, следующим образом:

public class CharsetFactoryMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var charset = context.Request.Query["charset"];
        context.Response.OnStarting(
                () => {
                    if (string.IsNullOrWhiteSpace(charset))
                        context.Response.ContentType = "text/plain; charset=utf-8";
                    else
                        context.Response.ContentType = $"text/plain; charset={charset}";
                    return Task.CompletedTask;
                });
        await next(context);
    }
}

здесь RequestDelegate передается уже не в конструкторе, а в реализуемом методе InvokeAsync. Теперь этот middleware надо каким-то образом активировать.

Использование factory-based middleware

Чтобы компонент созданный на основе фабрики классов заработал, необходимо выполнить следующие действия:

1. Зарегистрировать класс middleware, как сервис (про сервисы и Dependency Injection говориться далее, поэтому пока — просто повторяем код):

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<CharsetFactoryMiddleware>();//регистрируем сервис

var app = builder.Build();

регистрация сервиса должна осуществляться до вызова метода builder.Build().

2. Использовать метод UseMiddleware, как мы делали ранее:

app.UseMiddleware<CharsetFactoryMiddleware>();

или использовать метод расширения для IApplicationBuilder.

Ниже представлен весь код приложения, использующего factory-based middleware

namespace FactoryBasedMiddleware
{
    public class CharsetFactoryMiddleware : IMiddleware
    {
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var charset = context.Request.Query["charset"];
            context.Response.OnStarting(
                    () =>
                    {
                        if (string.IsNullOrWhiteSpace(charset))
                            context.Response.ContentType = "text/plain; charset=utf-8";
                        else
                            context.Response.ContentType = $"text/plain; charset={charset}";
                        return Task.CompletedTask;
                    });
            await next(context);
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            builder.Services.AddTransient<CharsetFactoryMiddleware>();//регистрируем сервис

            var app = builder.Build();
            app.UseMiddleware<CharsetFactoryMiddleware>(); //используем middleware

            app.Run(async context =>
           {
               await context.Response.WriteAsync("Привет, мир");
           });

            app.Run();
        }
    }
}

Ограничения

К сожалению, при использовании factory-based middleware имеется ограничение, заключающееся в том, что при использовании метода UseMiddleware<T>() мы не можем использовать параметры. Например, следующий код:

public class CharsetFactoryMiddleware : IMiddleware
{
    private string _defaultCharset;

    public CharsetFactoryMiddleware(string defaultCharset) 
    {
        _defaultCharset = defaultCharset;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        .....
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        .....
        app.UseMiddleware<CharsetFactoryMiddleware>("win-1251");
        ....
        app.Run();
    }
}

вызовет ошибку.

Итого

Для создание middleware на базе фабрики классов необходимо реализовать интерфейс IMiddleware, имеющий всего один метод. Преимуществом такого подхода к построению middleware можно отнести строгую типизацию. При этом, ограничением является то, что при использовании фабричного метода активации Middleware становится недоступной передача произвольных параметров в middleware (можно передавать только зависимости из контейнера DI, но об этом мы поговорим позднее)

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