Escrever middleware do ASP.NET Core personalizado
Observação
Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Por Fiyaz Hasan, Rick Anderson e Steve Smith
O middleware é um software montado em um pipeline de aplicativo para manipular solicitações e respostas. O ASP.NET Core fornece um rico conjunto de componentes de middleware internos, mas em alguns cenários, talvez seja conveniente que você escreva um middleware personalizado.
Este tópico descreve como escrever middleware baseado em convenção. Para obter uma abordagem que usa tipagem forte e ativação por solicitação, confira Ativação de middleware baseada em fábrica em ASP.NET Core.
Classe de middleware
O middleware geralmente é encapsulado em uma classe e exposto com um método de extensão. Considere o middleware em linha a seguir, que define a cultura para a solicitação atual 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 em linha anterior realçado é usado para demonstrar a criação de um componente de middleware chamando Microsoft.AspNetCore.Builder.UseExtensions.Use. O método de extensão anterior Use
adiciona um delegado de middleware definido na linha ao pipeline de solicitação do aplicativo.
Há duas sobrecargas disponíveis para a extensão Use
:
- Uma usa um HttpContext e um
Func<Task>
. Invoque oFunc<Task>
sem parâmetros. - O outro método utiliza um
HttpContext
e um RequestDelegate. Invoque oRequestDelegate
passando oHttpContext
.
Prefira usar a última sobrecarga, pois ela salva duas alocações internas por solicitação que são necessárias ao usar a outra sobrecarga.
Teste o middleware ao transmitir a cultura. Por exemplo, solicite https://localhost:5001/?culture=es-es
.
Para obter suporte a localização interna do ASP.NET Core, confira 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 de tipo RequestDelegate.
- Um método público chamado
Invoke
ouInvokeAsync
. Esse método precisa:- Retornar um
Task
. - Aceitar um primeiro parâmetro do tipo HttpContext.
- Retornar um
Os parâmetros adicionais para o construtor e Invoke
/InvokeAsync
são preenchidos pela DI (injeção de dependência).
Normalmente, um método de extensão é criado para expor o middleware por meio 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 ao expor 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, utilizando a DI (injeção de dependência) 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 do aplicativo. Serviços de tempo de vida com escopo usados pelos construtores do middleware não são compartilhados com outros tipos de dependência inseridos durante cada solicitação. Para compartilhar um serviço com escopo entre seu serviço de middleware e serviços de outros tipos, adicione esses serviços à assinatura do método InvokeAsync
. O método InvokeAsync
pode aceitar parâmetros adicionais que são preenchidos pela injeção de dependência:
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 tempo de vida e registro contêm um exemplo completo de middleware com serviços de tempo de vida com escopo.
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 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
- Código de exemplo usado neste artigo
- Origem de UseExtensions no GitHub
- As opções de tempo de vida e registro contêm um exemplo completo de middleware com serviços de tempo de vida com escopo, transitórios e singleton.
- APROFUNDAMENTO: COMO O PIPELINE DE MIDDLEWARE DO ASP.NET CORE FOI CRIADO
- Middleware do ASP.NET Core
- Testar middleware do ASP.NET Core
- Migrar manipuladores e módulos HTTP para middleware do ASP.NET Core
- Inicialização de aplicativo no ASP.NET Core
- Solicitar recursos no ASP.NET Core
- Ativação de middleware baseada em alocador no ASP.NET Core
- Ativação de middleware com um contêiner de terceiros no ASP.NET Core
Por Rick Anderson e Steve Smith
O middleware é um software montado em um pipeline de aplicativo para manipular solicitações e respostas. O ASP.NET Core fornece um rico conjunto de componentes de middleware internos, mas em alguns cenários, talvez seja conveniente que você escreva um middleware personalizado.
Observação
Este tópico descreve como escrever middleware baseado em convenção. Para obter uma abordagem que usa tipagem forte e ativação por solicitação, confira Ativação de middleware baseada em fábrica em ASP.NET Core.
Classe de middleware
O middleware geralmente é encapsulado em uma classe e exposto com um método de extensão. Considere o middleware a seguir, que define a cultura para a solicitação atual 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 suporte a localização interna do ASP.NET Core, confira Globalização e localização no ASP.NET Core.
Teste o middleware ao transmitir a 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 de tipo RequestDelegate.
- Um método público chamado
Invoke
ouInvokeAsync
. Esse método precisa:- Retornar um
Task
. - Aceitar um primeiro parâmetro do tipo HttpContext.
- Retornar um
Os parâmetros adicionais para o construtor e Invoke
/InvokeAsync
são preenchidos pela DI (injeção de dependência).
Dependências de middleware
O middleware deve seguir o princípio de dependências explícitas ao expor suas dependências em seu construtor. O middleware é construído uma vez por tempo de vida do aplicativo. Confira a seção Dependências de middleware por solicitação se você precisar compartilhar serviços com middleware em uma solicitação.
Os componentes de middleware podem resolver suas dependências, utilizando a DI (injeção de dependência) por meio de parâmetros do construtor. UseMiddleware também pode aceitar parâmetros adicionais diretamente.
Dependências de middleware por solicitação
Uma vez que o middleware é construído durante a inicialização do aplicativo, e não por solicitação, os serviços de tempo de vida com escopo usados pelos construtores do middleware não são compartilhados com outros tipos de dependência inseridos durante cada solicitação. Se você tiver que compartilhar um serviço com escopo entre seu serviço de middleware e serviços de outros tipos, adicione esses serviços à assinatura do método InvokeAsync
. O método InvokeAsync
pode aceitar parâmetros adicionais que são preenchidos pela injeção de dependência:
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 tempo de vida e registro contêm um exemplo completo de middleware com serviços de tempo de vida com escopo.
Método de extensão de middleware
O seguinte método de extensão expõe o middleware por meio do 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
- As opções de tempo de vida e registro contêm um exemplo completo de middleware com serviços de tempo de vida com escopo, transitórios e singleton.
- Middleware do ASP.NET Core
- Testar middleware do ASP.NET Core
- Migrar manipuladores e módulos HTTP para middleware do ASP.NET Core
- Inicialização de aplicativo no ASP.NET Core
- Solicitar recursos no ASP.NET Core
- Ativação de middleware baseada em alocador no ASP.NET Core
- Ativação de middleware com um contêiner de terceiros no ASP.NET Core