ASP.NET Core Avançado: Como Construir Middlewares Customizados e Dominar o Endpoint Routing para Alta Performance

quarta-feira, 28 de outubro de 2026 | ReadingTime
ASP.NET Core Avançado: Como Construir Middlewares Customizados e Dominar o Endpoint Routing para Alta Performance

No desenvolvimento de APIs de alta vazão com ASP.NET Core, a eficiência de execução da requisição HTTP é diretamente proporcional à maturidade da sua pipeline. Cada requisição trafega por uma cadeia bidirecional de componentes encadeados — o pipeline de middlewares — interagindo continuamente com a abstração central do runtime: o objeto HttpContext.


O entendimento detalhado da ordem de execução desses componentes e a utilização adequada do mecanismo de Endpoint Routing deixaram de ser itens opcionais para se tornarem requisitos arquiteturais básicos. A má configuração dessa cadeia gera gargalos de latência, vazamentos de memória (memory leaks) por alocações indevidas na Heap e falhas de segurança críticas quando etapas de autenticação ou auditoria são executadas fora de ordem.


Neste artigo, vamos analisar como construir middlewares customizados focados em baixa alocação e como utilizar o Endpoint Routing para inspecionar metadados antes da execução do código final da sua aplicação.


1. Criando Middlewares Customizados de Alta Performance


Existem duas abordagens para a construção de middlewares no ASP.NET Core: por Convenção e por Interface (IMiddleware).


Middleware por Convenção vs. Baseado em Interface


  • Por Convenção (Singleton-like / Injeção por Método): O middleware é instanciado uma única vez no momento da inicialização da aplicação (tempo de vida singleton). Dependências transient ou scoped devem ser injetadas diretamente no método InvokeAsync(HttpContext context, ...) e não no construtor. Essa abordagem é ideal para rotas de tráfego intenso por evitar a criação repetitiva de objetos a cada requisição.
  • Baseado em Interface (IMiddleware / Scoped): Requer a implementação da interface IMiddleware e o registro explícito do componente no contêiner de Injeção de Dependência (DI). A instância é resolvida para cada requisição (scoped), facilitando o manuseio de dependências com o mesmo ciclo de vida, porém introduzindo uma sobrecarga sutil de resolução no contêiner IoC.

Exemplo Prático: Middleware de Medição de Latência e Tracing


O código abaixo demonstra a criação de um middleware por convenção otimizado para medir o tempo de execução de requisições HTTP e adicionar identificadores de correlação (Correlation-ID) sem alocações desnecessárias.


C#

namespace MasterStack.Architecture.Web.Middlewares; 

 

using System.Diagnostics; 

using Microsoft.AspNetCore.Http; 

using Microsoft.Extensions.Logging; 

 

public sealed class RequestTracingMiddleware 

{ 

    private readonly RequestDelegate _next; 

    private readonly ILogger<RequestTracingMiddleware> _logger; 

    private const string CorrelationIdHeader = "X-Correlation-ID"; 

 

    public RequestTracingMiddleware(RequestDelegate next, ILogger<RequestTracingMiddleware> logger) 

    { 

        _next = next ?? throw new ArgumentNullException(nameof(next)); 

        _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 

    } 

 

    public async Task InvokeAsync(HttpContext context) 

    { 

        // 1. Extrai ou gera o Correlation ID utilizando tratamento de strings sem alocação pesada 

        if (!context.Request.Headers.TryGetValue(CorrelationIdHeader, out var correlationId) || string.IsNullOrWhiteSpace(correlationId)) 

        { 

            correlationId = Guid.NewGuid().ToString("N"); 

            context.Request.Headers[CorrelationIdHeader] = correlationId; 

        } 

 

        // Adiciona o cabeçalho na resposta antes que os cabeçalhos sejam enviados ao cliente 

        context.Response.OnStarting(() => 

        { 

            context.Response.Headers[CorrelationIdHeader] = correlationId; 

            return Task.CompletedTask; 

        }); 

 

        // 2. Medição precisa de tempo com ValueStopwatch ou Stopwatch de alta precisão 

        var startTimestamp = Stopwatch.GetTimestamp(); 

 

        try 

        { 

            await _next(context); 

        } 

        finally 

        { 

            var delta = Stopwatch.GetElapsedTime(startTimestamp); 

             

            _logger.LogInformation( 

                "HTTP {Method} {Path} respondeu {StatusCode} em {ElapsedMs:F2}ms [CorrelationId: {CorrelationId}]", 

                context.Request.Method, 

                context.Request.Path, 

                context.Response.StatusCode, 

                delta.TotalMilliseconds, 

                correlationId.ToString()); 

        } 

    } 

} 

 

// Método de Extensão Idiomático 

public static class RequestTracingMiddlewareExtensions 

{ 

    public static IApplicationBuilder UseRequestTracing(this IApplicationBuilder builder) 

    { 

        ArgumentNullException.ThrowIfNull(builder); 

        return builder.UseMiddleware<RequestTracingMiddleware>(); 

    } 

} 


2. Dominando o Endpoint Routing no ASP.NET Core


A partir do .NET Core 3.0 e aprimorado no .NET 8 e .NET 9, o Endpoint Routing desassociou a etapa de seleção de rotas da execução dos controladores de API ou Minimal APIs. O processo é dividido em duas fases distintas:


  1. UseRouting (Matcher): Analisa a requisição HTTP (URL, Method, Headers), encontra a rota correspondente e atribui o objeto Endpoint ao HttpContext.GetEndpoint().
  2. UseEndpoints ou Minimal APIs (Executor): Executa o delegate configurado para o endpoint selecionado.

Request HTTP ──► [ UseRouting ] ──► (Endpoint Selecionado / Metadados) ──► [ Middlewares Intermediários ] ──► [ Execution Endpoint ] 


A grande vantagem arquitetural desse modelo é que middlewares posicionados entre UseRouting e o executor final conseguem inspecionar os metadados do endpoint (EndpointMetadata) antes de processar qualquer lógica pesada.


Exemplo Prático: Restrição de Rota Customizada (IRouteConstraint)


O exemplo a seguir demonstra a criação de uma restrição de rota que valida se o parâmetro fornecido na URL é um identificador no formato CUS (Customer Unique System) válido sem precisar carregar o controlador.


C#

namespace MasterStack.Architecture.Web.Constraints; 

 

using Microsoft.AspNetCore.Http; 

using Microsoft.AspNetCore.Routing; 

 

public sealed class CustomerIdRouteConstraint : IRouteConstraint 

{ 

    public bool Match( 

        HttpContext? httpContext, 

        IRouter? route, 

        string routeKey, 

        RouteValueDictionary values, 

        RouteDirection routeDirection) 

    { 

        if (!values.TryGetValue(routeKey, out var value) || value is null) 

        { 

            return false; 

        } 

 

        var customerId = value.ToString(); 

         

        // Exemplo: Valida se o ID inicia com "CUS-" seguido por dígitos 

        if (string.IsNullOrEmpty(customerId) || !customerId.StartsWith("CUS-", StringComparison.OrdinalIgnoreCase)) 

        { 

            return false; 

        } 

 

        return customerId.Length > 4 && long.TryParse(customerId.AsSpan(4), out _); 

    } 

} 




Registro e Uso nas Minimal APIs ou Controllers:




C#

var builder = WebApplication.CreateBuilder(args); 

 

builder.Services.Configure<RouteOptions>(options => 

{ 

    options.ConstraintMap.Add("validCustomerId", typeof(CustomerIdRouteConstraint)); 

}); 

 

var app = builder.Build(); 

 

app.UseRouting(); 

 

// O Endpoint Routing rejeitará chamadas que não satisfaçam a restrição com HTTP 404 automaticamente 

app.MapGet("/api/customers/{id:validCustomerId}", (string id) =>  

{ 

    return Results.Ok(new { CustomerId = id, Status = "Active" }); 

}); 

 

app.Run(); 


3. Casos de Uso Reais, Cuidados e Performance (FinOps e SRE)


A Ordem Importa: O Impacto na Segurança e Latência


A ordem de registro dos middlewares define o comportamento do sistema. Um erro comum é registrar o tratamento global de exceções após a etapa de autenticação, o que pode fazer com que falhas ocorridas na validação do token retornem respostas sem o devido formato de erro padrão.


✅ Ordem Correta da Pipeline: 

1. Exception Handling (Catch global) 

2. HSTS / HTTPS Redirection 

3. Static Files 

4. Routing (UseRouting - Mapeia o endpoint e carrega metadados) 

5. CORS 

6. Authentication (Verifica a identidade) 

7. Authorization (Verifica as permissões com base nos metadados do endpoint) 

8. Custom Business Middlewares 

9. Endpoint Execution (UseEndpoints / MapEndpoints) 


Otimizações de Memória para Baixa Latência


  • Evite Leitura Síncrona do Stream do Corpo da Requisição: O acesso ao context.Request.Body sem buffer assíncrono bloqueia threads da ThreadPool. Utilize context.Request.EnableBuffering() apenas quando for estritamente necessário reler o corpo da requisição e utilize rotinas ReadAsync().
  • Atenção com Instanciações no Método InvokeAsync: Não instancie coleções, parsers JSON ou objetos de serviço dentro do método InvokeAsync. Mantenha esses objetos estáticos ou reaproveitáveis para evitar a pressão sobre as Gerações 0 e 1 do Garbage Collector (GC).


Onde usar vs. Onde EVITAR


Manipulação do Cabeçalho HTTP (CORS, Correlation-ID)

  • Middleware Global: ✅ Sim
  • Filter / Endpoint Filter: ❌ Não

Log e Métricas Globais de Latência

  • Middleware Global: ✅ Sim
  • Filter / Endpoint Filter: ❌ Não

Validação de Modelo de Domínio Específico

  • Middleware Global: ❌ Não
  • Filter / Endpoint Filter: ✅ Sim

Autorização baseada em Roles específicas da Action

  • Middleware Global: ❌ Não
  • Filter / Endpoint Filter: ✅ Sim



Conclusão


Dominar a arquitetura da pipeline HTTP e do Endpoint Routing no ASP.NET Core permite construir sistemas resilientes, seguros e de altíssimo desempenho. A aplicação correta de middlewares por convenção e restrições de rota evita o processamento desnecessário na CPU e reduz a alocação de memória no Heap.


👉 Pronto para levar a sua carreira em engenharia .NET para o próximo nível? Explore conteúdos avançados e conheça as melhores vagas para desenvolvedores na MasterStack Jobs. Conectamos talentos seniores às empresas mais inovadoras do mercado!

Marcio Mazeu
Escrito por
Marcio Mazeu

.NET Software Developer

Analista-Programador .NET graduado em Engenharia de Computação, especializado na modernização de aplicações e arquitetura de APIs (C#, SQL Server, JavaScript). Apaixonado pelo desenvolvimento de ferramentas de alta performance e pelo aprendizado contínuo, contribui para a criação de soluções tecnológicas confiáveis e focadas na experiência do usuário.