Partilhar via


Escrever middleware ASP.NET Core personalizado

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Advertência

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Por Fiyaz Hasan, Rick Anderson e Steve Smith

Middleware é um software montado em um pipeline de aplicativos para lidar com solicitações e respostas. ASP.NET Core fornece um conjunto avançado de componentes de middleware integrados, mas em alguns cenários você pode querer escrever um middleware personalizado.

Este tópico descreve como escrever middleware baseado em convenção. Para obter uma abordagem que usa digitação forte e ativação por solicitação, consulte Ativação de middleware baseada em fábrica no ASP.NET Core.

Classe middleware

O middleware geralmente é encapsulado em uma classe e exposto com um método de extensão. Considere o seguinte middleware embutido, que define a cultura para a solicitação atual a partir de uma cadeia de caracteres de consulta:

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

O middleware inline destacado anteriormente é usado para demonstrar a criação de um componente de middleware ao chamar Microsoft.AspNetCore.Builder.UseExtensions.Use. O método de extensão anterior Use adiciona um delegado de middleware definido diretamente ao pipeline de solicitação do aplicativo.

Há duas sobrecargas disponíveis para a Use extensão:

  • Leva-se um HttpContext e um Func<Task>. Invoque o Func<Task> sem quaisquer parâmetros.
  • O outro leva um HttpContext e um RequestDelegate. Invoque o RequestDelegate passando HttpContext.

Prefira usar a sobrecarga posterior, pois ela salva duas alocações internas por solicitação que são necessárias ao usar a outra sobrecarga.

Teste o middleware passando na cultura. Por exemplo, solicite https://localhost:5001/?culture=es-es.

Para obter o suporte interno à localização do ASP.NET Core, consulte Globalização e localização no ASP.NET Core.

O código a seguir move o delegado de middleware para uma classe:

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

A classe de middleware deve incluir:

  • Um construtor público com um parâmetro do tipo RequestDelegate.
  • Um método público chamado Invoke ou InvokeAsync. Este método deve:
    • Devolver um Taskficheiro .
    • Aceite um primeiro parâmetro do tipo HttpContext.

Parâmetros adicionais para o construtor e Invoke/InvokeAsync são preenchidos por injeção de dependência (DI).

Normalmente, um método de extensão é criado para expor o middleware através de 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>();
    }
}

O código a seguir chama o middleware de 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();

Dependências de middleware

O middleware deve seguir o princípio de dependências explícitas expondo suas dependências em seu construtor. O middleware é construído uma vez por tempo de vida do aplicativo.

Os componentes de middleware podem resolver suas dependências a partir da injeção de dependência (DI) por meio de parâmetros do construtor. UseMiddleware também pode aceitar parâmetros adicionais diretamente.

Dependências de middleware por solicitação

O middleware é construído na inicialização do aplicativo e, portanto, tem tempo de vida útil do aplicativo. Os serviços de ciclo de vida com escopo usados por construtores de middleware não são compartilhados com outros tipos injetados por dependência durante cada solicitação. Para compartilhar um serviço com escopo entre middleware e outros tipos, adicione esses serviços à assinatura do InvokeAsync método. O InvokeAsync método pode aceitar parâmetros adicionais que são preenchidos por 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>();
    }
}

As opções de ciclo de vida e registo contêm uma amostra completa de middleware com serviços com ciclo de vida delimitado.

O código a seguir é usado para testar o middleware anterior:

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

A interface IMessageWriter e a sua implementação

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

Recursos adicionais

Por Rick Anderson e Steve Smith

Middleware é um software montado em um pipeline de aplicativos para lidar com solicitações e respostas. ASP.NET Core fornece um conjunto avançado de componentes de middleware integrados, mas em alguns cenários você pode querer escrever um middleware personalizado.

Observação

Este tópico descreve como escrever middleware baseado em convenção. Para obter uma abordagem que usa digitação forte e ativação por solicitação, consulte Ativação de middleware baseada em fábrica no ASP.NET Core.

Classe middleware

O middleware geralmente é encapsulado em uma classe e exposto com um método de extensão. Considere o seguinte middleware, que define a cultura para a solicitação atual a partir de uma cadeia de caracteres de consulta:

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

    }
}

O código de exemplo anterior é usado para demonstrar a criação de um componente de middleware. Para obter o suporte interno à localização do ASP.NET Core, consulte Globalização e localização no ASP.NET Core.

Teste o middleware passando na cultura. Por exemplo, solicite https://localhost:5001/?culture=no.

O código a seguir move o delegado de middleware para uma classe:

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

A classe de middleware deve incluir:

  • Um construtor público com um parâmetro do tipo RequestDelegate.
  • Um método público chamado Invoke ou InvokeAsync. Este método deve:
    • Devolver um Taskficheiro .
    • Aceite um primeiro parâmetro do tipo HttpContext.

Parâmetros adicionais para o construtor e Invoke/InvokeAsync são preenchidos por injeção de dependência (DI).

Dependências de middleware

O middleware deve seguir o princípio de dependências explícitas expondo suas dependências em seu construtor. O middleware é construído uma vez por tempo de vida do aplicativo. Consulte a seção Dependências de middleware por solicitação se precisar compartilhar serviços com middleware em uma solicitação.

Os componentes de middleware podem resolver suas dependências a partir da injeção de dependência (DI) por meio de parâmetros do construtor. UseMiddleware também pode aceitar parâmetros adicionais diretamente.

Dependências de middleware por solicitação

Como o middleware é construído na inicialização do aplicativo, não por solicitação, os serviços de tempo de vida com escopo usados pelos construtores de middleware não são compartilhados com outros tipos de dependência injetada durante cada solicitação. Se tiver de partilhar um serviço de âmbito limitado entre o seu middleware e outros tipos, adicione esses serviços à assinatura do método InvokeAsync. O InvokeAsync método pode aceitar parâmetros adicionais que são preenchidos por 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);
    }
}

As opções de ciclo de vida e registo contêm uma amostra completa de middleware com serviços com ciclo de vida delimitado.

Método de extensão de middleware

O seguinte método de extensão expõe o middleware através de IApplicationBuilder:

using Microsoft.AspNetCore.Builder;

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

O código a seguir chama o middleware de Startup.Configure:

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

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

Recursos adicionais