Написание пользовательского ПО промежуточного слоя ASP.NET Core

Авторы: Фияз Хасан (Fiyaz Hasan), Рик Андерсон (Rick Anderson) и Стив Смит (Steve Smith)

ПО промежуточного слоя — это программное обеспечение, выстраиваемое в виде конвейера приложения для обработки запросов и откликов. ASP.NET Core предоставляет широкий набор встроенных компонентов ПО промежуточного слоя, но в некоторых случаях может потребоваться написать пользовательское ПО промежуточного слоя.

В этом разделе показано, как разработать ПО промежуточного слоя convention-based. Дополнительные сведения о подходе, использующем строгую типизацию и активацию по запросу, см. в статье Активация ПО промежуточного слоя на основе фабрики в ASP.NET Core.

Класс ПО промежуточного слоя

ПО промежуточного слоя обычно инкапсулируется в класс и предоставляется с помощью метода расширения. Рассмотрим следующее встроенное ПО промежуточного слоя, которое задает язык и региональные параметры для текущего запроса из строки запроса.

using System.Globalization;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseHttpsRedirection();

app.Use(async (context, next) =>
{
    var cultureQuery = context.Request.Query["culture"];
    if (!string.IsNullOrWhiteSpace(cultureQuery))
    {
        var culture = new CultureInfo(cultureQuery);

        CultureInfo.CurrentCulture = culture;
        CultureInfo.CurrentUICulture = culture;
    }

    // Call the next delegate/middleware in the pipeline.
    await next(context);
});

app.Run(async (context) =>
{
    await context.Response.WriteAsync(
        $"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});

app.Run();

Выделенное выше встроенное ПО промежуточного слоя используется для демонстрации создания компонента ПО промежуточного слоя путем вызова Microsoft.AspNetCore.Builder.UseExtensions.Use. Предыдущий метод расширения Use добавляет определенный встроенный делегат ПО промежуточного слоя к конвейеру запросов приложения.

Для расширения Use доступны две перегрузки:

  • Одна принимает HttpContext и Func<Task>. Вызовите Func<Task> без параметров.
  • Другая принимает HttpContext и RequestDelegate. Вызовите RequestDelegate, передав HttpContext.

Предпочтительно использовать последнюю перегрузку, так как она экономит два внутренних выделения для каждого запроса, которые требуются при использовании другой перегрузки.

Протестируйте ПО промежуточного слоя, передав язык и региональные параметры. Например, выполните запрос https://localhost:5001/?culture=es-es.

Дополнительные сведения о встроенной поддержке локализации ASP.NET Core см. в статье Глобализация и локализация в ASP.NET Core.

Следующий код перемещает делегат ПО промежуточного слоя в класс.

using System.Globalization;

namespace Middleware.Example;

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);

            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }

        // Call the next delegate/middleware in the pipeline.
        await _next(context);
    }
}

Класс ПО промежуточного слоя должен включать следующее:

  • Открытый конструктор с параметром типа RequestDelegate.
  • Открытый метод с именем Invoke или InvokeAsync. Этот метод должен:
    • вернуть Task;
    • принять первый параметр типа HttpContext.

Дополнительные параметры для конструктора и Invoke/InvokeAsync заполняются с помощью внедрения зависимости.

Как правило, метод расширения создается для предоставления ПО промежуточного слоя через IApplicationBuilder:

using System.Globalization;

namespace Middleware.Example;

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);

            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }

        // Call the next delegate/middleware in the pipeline.
        await _next(context);
    }
}

public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}

Следующий код вызывает ПО промежуточного слоя из Program.cs.

using Middleware.Example;
using System.Globalization;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseHttpsRedirection();

app.UseRequestCulture();

app.Run(async (context) =>
{
    await context.Response.WriteAsync(
        $"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});

app.Run();

Зависимости ПО промежуточного слоя

ПО промежуточного слоя должно соответствовать принципу явных зависимостей, предоставляя свои зависимости в своем конструкторе. ПО промежуточного слоя создается один раз за время существования приложения.

Компоненты промежуточного слоя могут разрешать свои зависимости, возникшие в результате внедрения зависимостей, за счет параметров конструктора. UseMiddleware также может принимать дополнительные параметры напрямую.

Зависимости ПО промежуточного слоя для отдельных запросов

ПО промежуточного слоя создается при запуске приложения и, следовательно, имеет время существования приложения. Службы с ограниченным временем существованием, используемые конструкторами ПО промежуточного слоя, не передаются другим типам с внедренными зависимостями во время каждого запроса. Чтобы совместно использовать службу с ограничением между ПО промежуточного слоя и другими типами, добавьте ее в сигнатуру метода InvokeAsync. Метод InvokeAsync может принимать дополнительные параметры, заполняемые при внедрении зависимостей.

namespace Middleware.Example;

public class MyCustomMiddleware
{
    private readonly RequestDelegate _next;

    public MyCustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    // IMessageWriter is injected into InvokeAsync
    public async Task InvokeAsync(HttpContext httpContext, IMessageWriter svc)
    {
        svc.Write(DateTime.Now.Ticks.ToString());
        await _next(httpContext);
    }
}

public static class MyCustomMiddlewareExtensions
{
    public static IApplicationBuilder UseMyCustomMiddleware(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyCustomMiddleware>();
    }
}

Параметры времени существования и регистрации — раздел содержит полный пример ПО промежуточного слоя со службами, имеющими время существования scoped (с заданной областью).

Следующий код используется для тестирования ПО промежуточного слоя:

using Middleware.Example;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IMessageWriter, LoggingMessageWriter>();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseMyCustomMiddleware();

app.MapGet("/", () => "Hello World!");

app.Run();

Реализация и интерфейс IMessageWriter:

namespace Middleware.Example;

public interface IMessageWriter
{
    void Write(string message);
}

public class LoggingMessageWriter : IMessageWriter
{

    private readonly ILogger<LoggingMessageWriter> _logger;

    public LoggingMessageWriter(ILogger<LoggingMessageWriter> logger) =>
        _logger = logger;

    public void Write(string message) =>
        _logger.LogInformation(message);
}

Дополнительные ресурсы

Авторы: Рик Андерсон (Rick Anderson) и Стив Смит (Steve Smith)

ПО промежуточного слоя — это программное обеспечение, выстраиваемое в виде конвейера приложения для обработки запросов и откликов. ASP.NET Core предоставляет широкий набор встроенных компонентов ПО промежуточного слоя, но в некоторых случаях может потребоваться написать пользовательское ПО промежуточного слоя.

Примечание.

В этом разделе показано, как разработать ПО промежуточного слоя convention-based. Дополнительные сведения о подходе, использующем строгую типизацию и активацию по запросу, см. в статье Активация ПО промежуточного слоя на основе фабрики в ASP.NET Core.

Класс ПО промежуточного слоя

ПО промежуточного слоя обычно инкапсулируется в класс и предоставляется с помощью метода расширения. Рассмотрим следующее ПО промежуточного слоя, которое задает язык и региональные параметры для текущего запроса из строки запроса.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            var cultureQuery = context.Request.Query["culture"];
            if (!string.IsNullOrWhiteSpace(cultureQuery))
            {
                var culture = new CultureInfo(cultureQuery);

                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;
            }

            // Call the next delegate/middleware in the pipeline
            await next();
        });

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });

    }
}

Приведенный выше пример кода демонстрирует создание компонента промежуточного слоя. Дополнительные сведения о встроенной поддержке локализации ASP.NET Core см. в статье Глобализация и локализация в ASP.NET Core.

Протестируйте ПО промежуточного слоя, передав язык и региональные параметры. Например, выполните запрос https://localhost:5001/?culture=no.

Следующий код перемещает делегат ПО промежуточного слоя в класс.

using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;

namespace Culture
{
    public class RequestCultureMiddleware
    {
        private readonly RequestDelegate _next;

        public RequestCultureMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var cultureQuery = context.Request.Query["culture"];
            if (!string.IsNullOrWhiteSpace(cultureQuery))
            {
                var culture = new CultureInfo(cultureQuery);

                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;

            }

            // Call the next delegate/middleware in the pipeline
            await _next(context);
        }
    }
}

Класс ПО промежуточного слоя должен включать следующее:

  • Открытый конструктор с параметром типа RequestDelegate.
  • Открытый метод с именем Invoke или InvokeAsync. Этот метод должен:
    • вернуть Task;
    • принять первый параметр типа HttpContext.

Дополнительные параметры для конструктора и Invoke/InvokeAsync заполняются с помощью внедрения зависимости.

Зависимости ПО промежуточного слоя

ПО промежуточного слоя должно соответствовать принципу явных зависимостей, предоставляя свои зависимости в своем конструкторе. ПО промежуточного слоя создается один раз за время существования приложения. В разделе Зависимости отдельных запросов приведены сведения о том, как использовать службы совместно с ПО промежуточного слоя внутри запроса.

Компоненты промежуточного слоя могут разрешать свои зависимости, возникшие в результате внедрения зависимостей, за счет параметров конструктора. UseMiddleware также может принимать дополнительные параметры напрямую.

Зависимости ПО промежуточного слоя для отдельных запросов

Так как ПО промежуточного слоя создается при запуске приложения, а не для отдельных запросов, службы времени существования scoped, используемые конструкторами ПО промежуточного слоя, не являются общими с другими типами, возникшими в результате внедрения зависимостей, в каждом из запросов. Если необходимо предоставить службу scoped для совместного использования ПО промежуточного слоя и другими типами, добавьте ее в сигнатуру метода InvokeAsync. Метод InvokeAsync может принимать дополнительные параметры, заполняемые при внедрении зависимостей.

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

    public CustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    // IMyScopedService is injected into InvokeAsync
    public async Task InvokeAsync(HttpContext httpContext, IMyScopedService svc)
    {
        svc.MyProperty = 1000;
        await _next(httpContext);
    }
}

Параметры времени существования и регистрации — раздел содержит полный пример ПО промежуточного слоя со службами, имеющими время существования scoped (с заданной областью).

Метод расширения ПО промежуточного слоя

Следующий метод расширения предоставляет ПО промежуточного слоя посредством IApplicationBuilder:

using Microsoft.AspNetCore.Builder;

namespace Culture
{
    public static class RequestCultureMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestCulture(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestCultureMiddleware>();
        }
    }
}

Следующий код вызывает ПО промежуточного слоя из Startup.Configure.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRequestCulture();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });
    }
}

Дополнительные ресурсы