Поделиться через


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

Примечание.

Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.

Предупреждение

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущей версии см. версию .NET 10 этой статьи.

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

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

В этой теме описывается, как написать посредническое ПО, основанное на соглашениях. Дополнительные сведения о подходе, использующем строгую типизацию и активацию по запросу, см. в статье Активация ПО промежуточного слоя на основе фабрики в 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 может принимать дополнительные параметры, заполняемые DI (внедрением зависимостей).

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.

Следующий код перемещает делегат middleware в класс.

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 может принимать дополнительные параметры, заполняемые с помощью DI (внедрения зависимостей).

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}");
        });
    }
}

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