Advanced ASP.NET Core: Building Custom Middleware and Mastering Endpoint Routing for High Performance

Wednesday, October 28, 2026 | ReadingTime
Advanced ASP.NET Core: Building Custom Middleware and Mastering Endpoint Routing for High Performance

In high-throughput API development with ASP.NET Core, HTTP request execution efficiency is directly proportional to the maturity of your underlying pipeline. Every incoming request flows through a bidirectional chain of components—the middleware pipeline—continuously interacting with the runtime’s core abstraction: the HttpContext object.


Understanding the execution order of these components and properly configuring Endpoint Routing are fundamental architectural requirements. Misconfiguring this execution chain introduces severe latency bottlenecks, memory leaks caused by unnecessary heap allocations, and critical security vulnerabilities when authentication or auditing routines execute out of sequence.


In this technical article, we will examine how to build low-allocation custom middleware and leverage Endpoint Routing to inspect endpoint metadata before executing your application's handler code.


1. Building High-Performance Custom Middleware


There are two primary approaches to building custom middleware in ASP.NET Core: Convention-Based and Factory/Interface-Based (IMiddleware).


Convention-Based vs. Interface-Based (IMiddleware)


  • Convention-Based (Singleton-like / Method Injection): The middleware is instantiated once during application startup (singleton lifetime). Transient or scoped dependencies must be injected directly into the InvokeAsync(HttpContext context, ...) method rather than the constructor. This pattern is recommended for high-volume execution paths to avoid per-request object allocations.
  • Interface-Based (IMiddleware / Scoped): Requires implementing the IMiddleware interface and explicitly registering the class in the Dependency Injection (DI) container. A new instance is resolved for every HTTP request (scoped), simplifying dependency management for scoped services at the cost of minor IoC container resolution overhead.


Practical Example: Latency Measurement and Request Tracing Middleware


The following example illustrates a convention-based middleware optimized to measure HTTP request execution time and inject correlation identifiers (Correlation-ID) with minimal allocation overhead.


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. Extract or generate Correlation ID with minimal string allocation 

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

        { 

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

            context.Request.Headers[CorrelationIdHeader] = correlationId; 

        } 

 

        // Attach response header before headers are emitted to the client 

        context.Response.OnStarting(() => 

        { 

            context.Response.Headers[CorrelationIdHeader] = correlationId; 

            return Task.CompletedTask; 

        }); 

 

        // 2. Accurate latency measurement using high-precision Stopwatch timestamping 

        var startTimestamp = Stopwatch.GetTimestamp(); 

 

        try 

        { 

            await _next(context); 

        } 

        finally 

        { 

            var delta = Stopwatch.GetElapsedTime(startTimestamp); 

             

            _logger.LogInformation( 

                "HTTP {Method} {Path} responded {StatusCode} in {ElapsedMs:F2}ms [CorrelationId: {CorrelationId}]", 

                context.Request.Method, 

                context.Request.Path, 

                context.Response.StatusCode, 

                delta.TotalMilliseconds, 

                correlationId.ToString()); 

        } 

    } 

} 

 

// Idiomatic Extension Method 

public static class RequestTracingMiddlewareExtensions 

{ 

    public static IApplicationBuilder UseRequestTracing(this IApplicationBuilder builder) 

    { 

        ArgumentNullException.ThrowIfNull(builder); 

        return builder.UseMiddleware<RequestTracingMiddleware>(); 

    } 

} 


2. Mastering Endpoint Routing in ASP.NET Core


Introduced in .NET Core 3.0 and continuously refined through .NET 8 and .NET 9, Endpoint Routing decoupled route matching from endpoint execution (such as Controllers or Minimal APIs). The routing process operates in two distinct phases:


  1. UseRouting (Matcher): Inspects the HTTP request (URL, Method, Headers), matches the configured route, and attaches the selected Endpoint object to HttpContext.GetEndpoint(). 
  2. UseEndpoints or Minimal APIs (Executor): Invokes the delegate associated with the selected endpoint. 

HTTP Request ──► [ UseRouting ] ──► (Selected Endpoint / Metadata) ──► [ Intermediate Middlewares ] ──► [ Execution Endpoint ] 


The primary architectural advantage of this design is that middleware placed between UseRouting and the final execution endpoint can inspect endpoint metadata (EndpointMetadata) prior to executing heavy business logic.


Practical Example: Custom Route Constraint (IRouteConstraint)


The following code demonstrates a custom route constraint that validates whether a URL parameter matches a valid Customer Unique System (CUS) identifier before matching the route to a controller or endpoint.


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

         

        // Example: Validate that the ID starts with "CUS-" followed by numerical digits 

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

        { 

            return false; 

        } 

 

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

    } 

} 




Registration and Minimal API Usage:




C#

var builder = WebApplication.CreateBuilder(args); 

 

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

{ 

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

}); 

 

var app = builder.Build(); 

 

app.UseRouting(); 

 

// Requests failing the constraint are automatically rejected with HTTP 404 before reaching endpoint handlers 

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

{ 

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

}); 

 

app.Run(); 


3. Real-World Use Cases, Pitfalls, and Performance Optimization (FinOps & SRE)


Ordering Matters: Impact on Security and Latency


The sequence of middleware registrations defines pipeline execution. A common mistake is placing exception-handling middleware after authentication. This mistake leads to unhandled exceptions during token validation, bypassing structured error response contracts.


✅ Recommended Pipeline Order: 

1. Exception Handling (Global catch) 

2. HSTS / HTTPS Redirection 

3. Static Files 

4. Routing (UseRouting - Route selection & metadata loading) 

5. CORS 

6. Authentication (Identity verification) 

7. Authorization (Permission validation against endpoint metadata) 

8. Custom Business Middlewares 

9. Endpoint Execution (UseEndpoints / MapEndpoints) 


Memory Optimizations for High-Throughput APIs


  • Avoid Synchronous Request Body Reads: Reading context.Request.Body synchronously starves the ThreadPool. Call context.Request.EnableBuffering() only when re-reading the stream is required, and perform asynchronous reads via ReadAsync().
  • Minimize Allocations in InvokeAsync: Do not instantiate transient objects, JSON parsers, or collection buffers inside the InvokeAsync execution path. Use static or pooled instances to avoid triggering Garbage Collector (GC) Generation 0/1 collections under load.


Where to Use vs. Where to AVOID


HTTP Header Manipulation (CORS, Correlation-ID)

  • Global Middleware: ✅ Yes
  • Endpoint / Action Filter: ❌ No

Global Request Logging & Latency Metrics

  • Global Middleware: ✅ Yes
  • Endpoint / Action Filter: ❌ No

Domain Model Validation

  • Global Middleware: ❌ No
  • Endpoint / Action Filter: ✅ Yes

Action-Specific Role Authorization

  • Global Middleware: ❌ No
  • Endpoint / Action Filter: ✅ Yes



Mastering the HTTP pipeline architecture and Endpoint Routing in ASP.NET Core enables engineers to build resilient, secure, and low-latency Web APIs. Properly utilizing convention-based middleware and custom route constraints prevents unnecessary CPU processing and keeps heap memory allocation to a minimum.


👉 Ready to take your .NET engineering career to the next level? Explore technical articles and top software engineering roles at MasterStack Jobs. We connect senior tech talent with leading engineering teams globally!

Marcio Mazeu
Written by
Marcio Mazeu

.NET Software Developer

.NET Software Developer with a degree in Computer Engineering, specialized in application modernization and API architecture (C#, SQL Server, JavaScript). Passionate about building high-performance tools and continuous learning, he contributes to creating reliable technological solutions focused on user experience.