MiddlewareGraph 1.0.0
dotnet add package MiddlewareGraph --version 1.0.0
NuGet\Install-Package MiddlewareGraph -Version 1.0.0
<PackageReference Include="MiddlewareGraph" Version="1.0.0" />
<PackageVersion Include="MiddlewareGraph" Version="1.0.0" />
<PackageReference Include="MiddlewareGraph" />
paket add MiddlewareGraph --version 1.0.0
#r "nuget: MiddlewareGraph, 1.0.0"
#:package MiddlewareGraph@1.0.0
#addin nuget:?package=MiddlewareGraph&version=1.0.0
#tool nuget:?package=MiddlewareGraph&version=1.0.0
MiddlewareGraph
ASP.NET Core Middleware Pipeline Visualizer & Inspector
MiddlewareGraph gives you complete visibility into your ASP.NET Core middleware pipeline. No more guessing what middleware is registered, in what order, or how long each one takes.
The Problem
ASP.NET Core's middleware pipeline is completely opaque:
- Zero visibility — No way to see the ordered middleware list without reading every line of Program.cs
- No isolation testing — Testing a single middleware requires spinning up a full TestServer
- No latency observability — Cannot see per-middleware execution time without custom wrappers
- Silent ordering bugs — Putting Authentication after Authorization silently breaks security
- Third-party black boxes — NuGet packages register middleware invisibly
Features
| Feature | Description |
|---|---|
| Pipeline Introspection | See every middleware in order with type, assembly, and category |
| Debug Dashboard | Interactive HTML dashboard at /middlewaregraph |
| Per-Middleware Latency | P50/P95/P99 timing per middleware with ring buffer |
| Ordering Validation | Catch Auth-before-AuthZ bugs at startup |
| Test Harness | Test any middleware in isolation — no TestServer needed |
| Request Flow Tracing | X-MiddlewareGraph-Trace header shows request path |
| Pipeline Diff | Compare middleware across environments |
| Health Checks | ASP.NET Core health check integration |
| Mermaid/PlantUML Export | Generate architecture diagrams automatically |
| Startup Time Breakdown | See which middleware registration is slow |
Quick Start (5 lines)
// Program.cs
builder.Services.AddMiddlewareGraph();
var app = builder.Build();
app.UseMiddlewareGraph(); // Add before other middleware
// ... rest of your pipeline
Visit https://localhost:5001/middlewaregraph to see the dashboard.
Installation
dotnet add package MiddlewareGraph
Full Configuration
builder.Services.AddMiddlewareGraph(options =>
{
// Dashboard
options.Dashboard.Path = "/middlewaregraph";
options.Dashboard.Enabled = true;
// Metrics — per-middleware latency tracking
options.Metrics.Enabled = true;
options.Metrics.RingBufferSize = 128;
// Validation — catch ordering bugs at startup
options.Validation.ThrowOnViolation = true;
options.Validation.Rules.RequireBefore<AuthenticationMiddleware, AuthorizationMiddleware>();
// Diagnostics — request flow tracing
options.Diagnostics.EnableRequestTracing = true;
});
API Reference
Querying the Pipeline
Inject IMiddlewareGraph anywhere:
public class MyService(IMiddlewareGraph graph)
{
public void Inspect()
{
// All registered middleware in order
var all = graph.Pipeline;
// Find specific middleware
var auth = graph.Find<AuthenticationMiddleware>();
// Check ordering
bool valid = graph.IsBefore<CorsMiddleware, RoutingMiddleware>();
// Get branches (from app.Map())
var branches = graph.GetBranches();
// Total count
int count = graph.Count;
}
}
Middleware Test Harness
Test any middleware in isolation — no TestServer, no WebApplicationFactory:
// Minimal test
var result = await MiddlewareTestHarness
.For<MyCustomMiddleware>()
.Build()
.SendAsync(HttpMethod.Get, "/test");
// Full test with DI and request configuration
await using var runner = MiddlewareTestHarness
.For<MyCustomMiddleware>()
.WithServices(services =>
{
services.AddSingleton<IMyDependency, MockDependency>();
})
.WithRequest(ctx =>
{
ctx.Request.Method = "POST";
ctx.Request.Path = "/api/orders";
ctx.Request.Headers["X-Tenant-Id"] = "tenant-123";
})
.WithNext(ctx =>
{
ctx.Response.StatusCode = 200;
return Task.CompletedTask;
})
.Build();
var context = await runner.SendAsync();
Assert.Equal(200, context.Response.StatusCode);
Assert.True(context.Response.Headers.ContainsKey("X-Processed-By"));
Test short-circuiting:
await using var runner = MiddlewareTestHarness
.For<AuthMiddleware>()
.WithRequest(ctx => ctx.Request.Path = "/blocked")
.WithPassthroughNext()
.Build();
var context = await runner.SendAsync();
Assert.Equal(403, context.Response.StatusCode);
Ordering Validation
Built-in rules catch common mistakes:
- Authentication must come before Authorization
- CORS must come before Routing
- Routing must come before Endpoints
- ExceptionHandler should be early in the pipeline
- HTTPS Redirection should be early in the pipeline
Add custom rules:
options.Validation.Rules
.RequireBefore<CorsMiddleware, RoutingMiddleware>()
.MustBeFirst<ExceptionHandlerMiddleware>()
.MustBeLast<EndpointMiddleware>();
Health Checks
builder.Services.AddHealthChecks()
.AddMiddlewareGraphHealth(options =>
{
options.DegradedThreshold = TimeSpan.FromMilliseconds(100);
options.UnhealthyThreshold = TimeSpan.FromMilliseconds(500);
});
Pipeline Diff
Compare middleware across environments:
// Save a snapshot (via /middlewaregraph/snapshot endpoint or programmatically)
var snapshot = PipelineSnapshot.Create(graph.Pipeline, "Production");
// Compare two snapshots
var diff = PipelineDiff.Compare(devSnapshot, prodSnapshot);
Console.WriteLine($"Added: {diff.Added.Count}");
Console.WriteLine($"Removed: {diff.Removed.Count}");
Console.WriteLine($"Reordered: {diff.Reordered.Count}");
Console.WriteLine($"Identical: {diff.AreIdentical}");
Export to Mermaid/PlantUML
// Generate Mermaid diagram for GitHub markdown
string mermaid = graph.ExportMermaid();
// Generate PlantUML diagram
string plantuml = graph.ExportPlantUml();
Or visit:
/middlewaregraph/mermaid— Mermaid flowchart syntax/middlewaregraph/plantuml— PlantUML activity diagram
Dashboard Endpoints
| Endpoint | Description |
|---|---|
GET /middlewaregraph |
Interactive HTML dashboard |
GET /middlewaregraph/json |
Pipeline as JSON |
GET /middlewaregraph/metrics |
Per-middleware latency metrics |
GET /middlewaregraph/mermaid |
Mermaid diagram export |
GET /middlewaregraph/plantuml |
PlantUML diagram export |
GET /middlewaregraph/snapshot |
Pipeline snapshot for diffing |
GET /middlewaregraph/startup |
Startup registration timings |
Dashboard Preview
┌─────────────────────────────────────────────────┐
│ MiddlewareGraph — Pipeline Inspector │
│ Pipeline | Metrics | Validation | Startup │
├─────────────────────────────────────────────────┤
│ │
│ Middleware Pipeline (8 registered) │
│ Request flows top → bottom │
│ │
│ 0 DashboardMiddleware [Application] │
│ 1 FlowTraceMiddleware [Application] │
│ 2 HttpsRedirection [Framework] 0.1ms │
│ 3 Routing [Framework] 0.3ms │
│ 4 CustomMiddleware [Application] 0.05ms │
│ 5 Authentication [Framework] 1.2ms │
│ 6 Authorization [Framework] 0.8ms │
│ 7 Endpoints [Framework] 2.1ms │
│ │
│ Validation: ✓ All rules passed │
│ │
│ Export: [JSON] [Mermaid] [PlantUML] [Snapshot] │
└─────────────────────────────────────────────────┘
Technical Details
- Zero external dependencies — Only Microsoft.AspNetCore.App framework reference
- Zero reflection at request time — Reflection only during startup for name extraction
- Thread-safe metrics — Lock-free ring buffer using
Interlockedoperations - Dashboard: pure HTML+CSS — No JavaScript frameworks
- Negligible overhead — ~450ns per request for timing (15 middleware × 30ns each)
- Dev-only by default — Dashboard only serves in Development environment
Architecture
MiddlewareGraph uses an IStartupFilter to wrap the IApplicationBuilder with an instrumenting decorator. Every Use() call is intercepted to:
- Extract the middleware type name via reflection on the delegate closure
- Classify it as Framework/ThirdParty/Application based on assembly
- Wrap the delegate with
Stopwatchtiming - Record the descriptor in the pipeline graph
The pipeline graph is frozen after startup and exposed as a singleton IMiddlewareGraph via DI.
Comparison
| Feature | MiddlewareGraph | MiddlewareAnalysis | Manual |
|---|---|---|---|
| Pipeline visualization | ✅ HTML Dashboard | ❌ | ❌ |
| Per-middleware latency | ✅ P50/P95/P99 | ❌ | Custom code |
| Ordering validation | ✅ Built-in + custom | ❌ | ❌ |
| Isolation test harness | ✅ Fluent API | ❌ | TestServer |
| Request flow tracing | ✅ Response header | DiagnosticSource | Custom |
| Pipeline diff | ✅ Cross-environment | ❌ | ❌ |
| Mermaid/PlantUML export | ✅ | ❌ | ❌ |
| Health checks | ✅ | ❌ | ❌ |
| External dependencies | None | None | N/A |
License
MIT
Version History
- v1.0.0 — Initial release: Pipeline introspection, dashboard, latency tracking, test harness, ordering validation, request tracing, pipeline diff, health checks, Mermaid/PlantUML export, startup timing
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net7.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 44 | 3/29/2026 |