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

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

ПО промежуточного слоя — это программное обеспечение, выстраиваемое в виде конвейера приложения для обработки запросов и откликов.Middleware is software that's assembled into an app pipeline to handle requests and responses. ASP.NET Core предоставляет широкий набор встроенных компонентов ПО промежуточного слоя, но в некоторых случаях может потребоваться написать пользовательское ПО промежуточного слоя.ASP.NET Core provides a rich set of built-in middleware components, but in some scenarios you might want to write a custom middleware.

Примечание

В этом разделе показано, как разработать ПО промежуточного слоя convention-based.This topic describes how to write convention-based middleware. Подход, использующий строгую типизацию и активацию по запросу, описан здесь: Активация ПО промежуточного слоя на основе фабрики в ASP.NET Core.For an approach that uses strong typing and per-request activation, see Активация ПО промежуточного слоя на основе фабрики в ASP.NET Core.

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

ПО промежуточного слоя обычно инкапсулируется в класс и предоставляется с помощью метода расширения.Middleware is generally encapsulated in a class and exposed with an extension method. Рассмотрим следующее ПО промежуточного слоя, которое задает язык и региональные параметры для текущего запроса из строки запроса.Consider the following middleware, which sets the culture for the current request from a query string:

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

    }
}

Приведенный выше пример кода демонстрирует создание компонента промежуточного слоя.The preceding sample code is used to demonstrate creating a middleware component. Дополнительные сведения о встроенной поддержке локализации в ASP.NET Core см. в разделе Глобализация и локализация в ASP.NET Core.For ASP.NET Core's built-in localization support, see Глобализация и локализация в ASP.NET Core.

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

Следующий код перемещает делегат ПО промежуточного слоя в класс.The following code moves the middleware delegate to a class:

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

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

  • Открытый конструктор с параметром типа RequestDelegate.A public constructor with a parameter of type RequestDelegate.
  • Открытый метод с именем Invoke или InvokeAsync.A public method named Invoke or InvokeAsync. Этот метод должен:This method must:
    • вернуть Task;Return a Task.
    • принять первый параметр типа HttpContext.Accept a first parameter of type HttpContext.

Дополнительные параметры для конструктора и Invoke/InvokeAsync заполняются с помощью внедрения зависимости.Additional parameters for the constructor and Invoke/InvokeAsync are populated by dependency injection (DI).

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

ПО промежуточного слоя должно соответствовать принципу явных зависимостей, предоставляя свои зависимости в своем конструкторе.Middleware should follow the Explicit Dependencies Principle by exposing its dependencies in its constructor. ПО промежуточного слоя создается один раз за время существования приложения.Middleware is constructed once per application lifetime. В разделе Зависимости отдельных запросов приведены сведения о том, как использовать службы совместно с ПО промежуточного слоя внутри запроса.See the Per-request middleware dependencies section if you need to share services with middleware within a request.

Компоненты промежуточного слоя могут разрешать свои зависимости, возникшие в результате внедрения зависимостей, за счет параметров конструктора.Middleware components can resolve their dependencies from dependency injection (DI) through constructor parameters. UseMiddleware<T> также может принимать дополнительные параметры напрямую.UseMiddleware<T> can also accept additional parameters directly.

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

Так как ПО промежуточного слоя создается при запуске приложения, а не для отдельных запросов, службы времени существования scoped, используемые конструкторами ПО промежуточного слоя, не являются общими с другими типами, возникшими в результате внедрения зависимостей, в каждом из запросов.Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. Если необходимо предоставить службу scoped для совместного использования ПО промежуточного слоя и другими типами, добавьте ее в сигнатуру метода Invoke.If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. Метод Invoke может принимать дополнительные параметры, заполняемые при внедрении зависимостей.The Invoke method can accept additional parameters that are populated by DI:

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

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

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

Параметры времени существования и регистрации — раздел содержит полный пример ПО промежуточного слоя со службами, имеющими время существования scoped (с заданной областью).Lifetime and registration options contains a complete sample of middleware with scoped lifetime services.

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

Следующий метод расширения предоставляет ПО промежуточного слоя посредством IApplicationBuilder:The following extension method exposes the middleware through IApplicationBuilder:

using Microsoft.AspNetCore.Builder;

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

Следующий код вызывает ПО промежуточного слоя из Startup.Configure.The following code calls the middleware from Startup.Configure:

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

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

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