Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Authentication/authorization middleware configuration -- see [skill:dotnet-api-security]
Observability middleware (OpenTelemetry, health checks) -- see [skill:dotnet-observability]
Minimal API endpoint filters -- see [skill:dotnet-minimal-apis]
Cross-references: [skill:dotnet-observability] for logging and telemetry middleware, [skill:dotnet-api-security] for
auth middleware, [skill:dotnet-minimal-apis] for endpoint filters (the Minimal API equivalent of middleware).
Pipeline Ordering
Middleware executes in the order it is registered. The order is critical -- placing middleware in the wrong position
causes subtle bugs (missing CORS headers, unhandled exceptions, auth bypasses).
Recommended Order
var app = builder.Build();
// 1. Exception handling (outermost -- catches everything below)
app.UseExceptionHandler("/error");
// 2. HSTS (before any response is sent)if (!app.Environment.IsDevelopment())
{
app.UseHsts();
}
// 3. HTTPS redirection
app.UseHttpsRedirection();
// 4. Static files (short-circuits for static content before routing)
app.UseStaticFiles();
// 5. Routing (matches endpoints but does not execute them yet)// .NET 6+ calls UseRouting() implicitly if omitted; shown here for clarity
app.UseRouting();
// 6. CORS (must be after routing, before auth)
app.UseCors();
// 7. Authentication (identifies the user)
app.UseAuthentication();
// 8. Authorization (checks permissions against the matched endpoint)
app.UseAuthorization();
app.UseRequestLogging();
app.MapControllers();
app.MapRazorPages();
```text
| Mistake | Consequence |
| ----------------------------------------------- | ----------------------------------------------------------- |
| `UseAuthorization()` before `UseRouting()` | Authorization has no endpoint metadata -- all requests pass |
| `UseCors()` after `UseAuthorization()` | Preflight requests fail because they lack auth tokens |
| `UseExceptionHandler()` after custom middleware | Exceptions custom middleware are unhandled |
| `UseStaticFiles()` after `UseAuthorization()` | Static files require authentication unnecessarily |
---
Convention-based middleware uses a constructor `RequestDelegate` an `InvokeAsync` method. This the standard
pattern reusable middleware.
```csharp
{
RequestDelegate _next;
ILogger<RequestTimingMiddleware> _logger;
{
_next = next;
_logger = logger;
}
{
stopwatch = Stopwatch.StartNew();
{
_next(context);
}
{
stopwatch.Stop();
_logger.LogInformation(
,
context.Request.Method,
context.Request.Path,
stopwatch.ElapsedMilliseconds,
context.Response.StatusCode);
}
}
}
{
=> app.UseMiddleware<RequestTimingMiddleware>();
}
app.UseRequestTiming();
```csharp
For middleware that requires services, implement `IMiddleware`. This uses DI to create middleware instances
per-request instead of once at startup:
```csharp
:
{
TenantDbContext _db;
{
_db = db;
}
{
tenantId = context.Request.Headers[]
.FirstOrDefault();
(tenantId )
{
tenant = _db.Tenants.FindAsync(tenantId);
context.Items[] = tenant;
}
next(context);
}
}
builder.Services.AddScoped<TenantMiddleware>();
app.UseMiddleware<TenantMiddleware>();
```text
**Convention-based vs IMiddleware:**
| Aspect | Convention-based | `IMiddleware` |
| --------------- | ------------------------------------------- | ---------------------------------------------------------------- |
| Lifetime | Singleton (created once) | Per-request ( DI) |
| Scoped services | Via `InvokeAsync` parameters only | Via constructor injection |
| Registration | `UseMiddleware<T>()` only | Requires `services.Add*<T>()` + `UseMiddleware<T>()` |
| Performance | =>
{
context.Response.Headers[] =
context.TraceIdentifier;
next(context);
});
```text
```csharp
app.Run( context =>
{
context.Response.WriteAsync();
});
```text
```csharp
app.Map(, diagnosticApp =>
{
diagnosticApp.Run( context =>
{
data =
{
MachineName = Environment.MachineName,
Timestamp = DateTimeOffset.UtcNow
};
context.Response.WriteAsJsonAsync(data);
});
});
```json
---
Middleware can -circuit the pipeline calling `next()`. Use early validation, rate limiting,
feature flags.
```csharp
{
RequestDelegate _next;
_expectedKey;
{
_next = next;
_expectedKey = config[]
?? InvalidOperationException(
);
}
{
(!context.Request.Headers.TryGetValue(
, providedKey)
|| !.Equals(
providedKey, _expectedKey, StringComparison.Ordinal))
{
context.Response.StatusCode =
StatusCodes.Status401Unauthorized;
context.Response.WriteAsJsonAsync(
{
Error =
});
;
}
_next(context);
}
}
```text
```csharp
app.UseWhen(
context => context.Request.Path.StartsWithSegments(),
betaApp =>
{
betaApp.Use( (context, next) =>
{
featureManager = context.RequestServices
.GetRequiredService<IFeatureManager>();
(! featureManager.IsEnabledAsync())
{
context.Response.StatusCode =
StatusCodes.Status404NotFound;
;
}
next(context);
});
});
```text
---
The request body a forward-only stream . Enable buffering to read it multiple times:
```csharp
{
RequestDelegate _next;
ILogger<RequestLoggingMiddleware> _logger;
{
_next = next;
_logger = logger;
}
{
context.Request.EnableBuffering();
(context.Request.ContentLength >
&& context.Request.ContentLength < _000)
{
context.Request.Body.Position = ;
reader = StreamReader(
context.Request.Body, leaveOpen: );
body = reader.ReadToEndAsync();
_logger.LogDebug(
,
context.Request.Path, body);
context.Request.Body.Position = ;
}
_next(context);
}
}
```text
To capture modify the response body, replace `context.Response.Body` a `MemoryStream`:
```
{
originalBodyStream = context.Response.Body;
responseBody = MemoryStream();
context.Response.Body = responseBody;
_next(context);
context.Response.Body.Seek(, SeekOrigin.Begin);
responseText = StreamReader(
context.Response.Body).ReadToEndAsync();
context.Response.Body.Seek(, SeekOrigin.Begin);
responseBody.CopyToAsync(originalBodyStream);
}
```text
**Caution:** Response body replacement adds memory overhead should only be used diagnostics specific
transformation requirements, high-throughput paths.
---
ASP.NET Core provides `UseExceptionHandler` production-grade exception handling. This should always be the outermost
middleware:
```csharp
app.UseExceptionHandler(exceptionApp =>
{
exceptionApp.Run( context =>
{
context.Response.StatusCode =
StatusCodes.Status500InternalServerError;
context.Response.ContentType = ;
exceptionFeature = context.Features
.Get<IExceptionHandlerFeature>();
logger = context.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogError(
exceptionFeature?.Error,
,
context.Request.Path);
context.Response.WriteAsJsonAsync(
{
Error = ,
TraceId = context.TraceIdentifier
});
});
});
```text
.NET introduced `IExceptionHandler` DI-friendly, composable exception handling. Multiple handlers can be
registered are invoked order until one handles the exception:
```csharp
:
{
{
(exception ValidationException validationException)
;
context.Response.StatusCode =
StatusCodes.Status400BadRequest;
context.Response.WriteAsJsonAsync(
{
Error = ,
Details = validationException.Errors
}, ct);
;
}
}
{
{
logger.LogError(exception, );
context.Response.StatusCode =
StatusCodes.Status500InternalServerError;
context.Response.WriteAsJsonAsync(
{
Error = ,
TraceId = context.TraceIdentifier
}, ct);
;
}
}
builder.Services.AddExceptionHandler<ValidationExceptionHandler>();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
app.UseExceptionHandler();
```text
;
app.UseStatusCodePages( context =>
{
context.HttpContext.Response.ContentType = ;
context.HttpContext.Response.WriteAsJsonAsync(
{
Error = ,
TraceId = context.HttpContext.TraceIdentifier
});
});
```text
---
`UseWhen` branches the pipeline based a predicate. The branch rejoins the main pipeline after execution:
```csharp
app.UseWhen(
context => context.Request.Path.StartsWithSegments(),
apiApp =>
{
apiApp.UseRateLimiter();
});
```text
`MapWhen` creates a terminal branch that does rejoin the main pipeline:
```csharp
app.MapWhen(
context => context.WebSockets.IsWebSocketRequest,
wsApp =>
{
wsApp.Run( context =>
{
ws = context.WebSockets
.AcceptWebSocketAsync();
});
});
```text
```
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
}
{
app.UseExceptionHandler();
app.UseHsts();
}
```text
---
- **Order everything** -- middleware executes top-to-bottom requests bottom-to-top responses; incorrect
order causes auth bypasses, missing headers, unhandled exceptions
- **Exception handler goes first** -- `UseExceptionHandler` must be the outermost middleware to exceptions
all downstream components
- **Prefer classes over inline reusable middleware** -- convention-based middleware classes are testable,
composable, follow the single-responsibility principle
- **Use `IMiddleware` dependencies** -- convention-based middleware singleton;
// 9. Custom middleware (runs after auth, before endpoint execution)
// 10. Endpoint execution (terminal -- executes the matched endpoint)
Slightly faster (no per-request allocation) | Resolved from DI each request (lifetime depends on registration) |
---
## Inline Middleware
For simple, one-off middleware logic, use `app.Use()`, `app.Map()`, or `app.Run()`:
### app.Use -- Pass-Through
```csharp
// Adds a header to every response, then passes to next middleware
app.Use(async (context, next)
"X-Request-Id"
await
### app.Run -- Terminal
// Terminal middleware -- does NOT call next
async
await
"Fallback response"
### app.Map -- Branch by Path
// Branch the pipeline for requests matching /api/diagnostics
For HTTP error status codes that are not caused byexceptions (404, 403), use `UseStatusCodePages`:
```csharp
app.UseStatusCodePagesWithReExecute("/error/{0}")
// Only apply rate limiting headers for API routes
"/api"
// Requires builder.Services.AddRateLimiter() in service registration
### MapWhen -- Conditional Branch (Does Not Rejoin)
not
// Serve a special handler for WebSocket upgrade requests
async
using
var
await
// Handle WebSocket connection
### Environment-Specific Middleware
csharp
if (app.Environment.IsDevelopment())
else
"/error"
## Key Principles
is
for
and
for
and
catch
from
for
and
for
scoped
is
if you need scopedservices
(DbContext, user-scoped caches), use `IMiddleware`
- **Short-circuit intentionally** -- always document why a middleware does not call `next()` and ensure it writes a
complete response
- **Avoid response body manipulation in hot paths** -- replacing `Response.Body` with `MemoryStream` doubles memory
usage per request
---
## Agent Gotchas
1. **Do not place `UseAuthorization()` before `UseRouting()`** -- authorization requires endpoint metadata from routing
to evaluate policies. Without routing, all authorization checks are skipped.
2. **Do not place `UseCors()` after `UseAuthorization()`** -- CORS preflight (OPTIONS) requests donot carry auth
tokens. If auth runs first, preflights are rejected with 401.
3. **Do not forget to call `next()` in pass-through middleware** -- forgetting `await _next(context)` silently
short-circuits the pipeline, causing downstream middleware and endpoints to never execute.
4. **Do not read `Request.Body` without `EnableBuffering()`** -- the request body stream is forward-only bydefault.
Reading it without buffering consumes it, causing model binding and subsequent reads to fail with empty data.
5. **Do not register `IMiddleware` implementations without DI registration** -- unlike convention-based middleware,
`IMiddleware` requires explicit `services.AddScoped<T>()` or `services.AddTransient<T>()`. Without it,
`UseMiddleware<T>()` throws at startup.
6. **Do not write to `Response.Body` after calling `next()` if downstream middleware has already started the response**
-- once headers are sent (response has started), modifications throw `InvalidOperationException`. Check
`context.Response.HasStarted` before writing.
---
## Knowledge Sources
Middleware patterns inthis skill are grounded in publicly available content from:
- **Andrew Lock's "Exploring ASP.NET Core" Blog Series** -- Deep coverage of middleware authoring patterns, including
IMiddleware vs convention-based trade-offs, pipeline ordering pitfalls, endpoint routing internals, and
IExceptionHandler composition. Source: https://andrewlock.net/
- **Official ASP.NET Core Middleware Documentation** -- Middleware fundamentals, factory-based activation, and error
handling patterns. Source: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/
> **Note:** This skill applies publicly documented guidance. It does not represent or speak for the named sources.
## References
- [ASP.NET Core middleware](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/)
- [Write custom ASP.NET Core middleware](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write)
- [Factory-based middleware activation](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/extensibility)
- [Handle errors in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling)
- [IExceptionHandler in .NET 8](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling#iexceptionhandler)
- [Exploring ASP.NET Core (Andrew Lock)](https://andrewlock.net/)
---
## Attribution
Adapted from [Aaronontheweb/dotnet-skills](https://github.com/Aaronontheweb/dotnet-skills) (MIT license).
Code Navigation (Serena MCP)
Primary approach: Use Serena symbol operations for efficient code navigation:
Find definitions: serena_find_symbol instead of text search
Understand structure: serena_get_symbols_overview for file organization
Track references: serena_find_referencing_symbols for impact analysis
Precise edits: serena_replace_symbol_body for clean modifications
When to use Serena vs traditional tools:
Use Serena: Navigation, refactoring, dependency analysis, precise edits
Use Read/Grep: Reading full files, pattern matching, simple text operations
Fallback: If Serena unavailable, traditional tools work fine