用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/rudironsoni/Synaxis --skill dotnet-middleware-patterns命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Routes .NET/C# work to domain skills. Loads coding-standards for code paths.
基于 SOC 职业分类
正在显示 SKILL.md
| name | dotnet-middleware-patterns |
| category | web |
| subcategory | middleware |
| description | Builds ASP.NET Core middleware. Pipeline ordering, short-circuit, exception handling. |
| license | MIT |
| targets | ["*"] |
| tags | ["foundation","dotnet","skill"] |
| version | 0.0.1 |
| author | dotnet-agent-harness |
| invocable | true |
| claudecode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| codexcli | {"short-description":".NET skill guidance for foundation tasks"} |
| opencode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| copilot | {} |
| geminicli | {} |
| antigravity | {} |
ASP.NET Core middleware patterns for the HTTP request pipeline. Covers correct ordering, writing custom middleware as classes or inline delegates, short-circuit logic, request/response manipulation, exception handling middleware, and conditional middleware registration.
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).
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).
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;
Primary approach: Use Serena symbol operations for efficient code navigation:
serena_find_symbol instead of text searchserena_get_symbols_overview for file organizationserena_find_referencing_symbols for impact analysisserena_replace_symbol_body for clean modificationsWhen to use Serena vs traditional tools:
Example workflow:
# Instead of:
Read: src/Services/OrderService.cs
Grep: "public void ProcessOrder"
# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"