ASP.NET Core 管道概览
了解请求如何流经中间件管道和 HttpContext,以及响应如何逐步构建。
ASP.NET Core 管道概览 是 CoddyKit 上的免费 C# Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
请求管道
ASP.NET Core 中的每个 HTTP 请求都会流经一个中间件管道。每个中间件都可以检查、修改、短路请求,或将请求转发给下一个组件。理解这个管道是构建 ASP.NET Core 应用的基础。
HttpContext:请求的封装对象
HttpContext 携带请求和响应的所有信息:标头、Cookie、正文、用户声明、连接信息以及取消令牌。所有中间件都在它上面运行。
app.Use(async (context, next) =>
{
// Read request info
var method = context.Request.Method;
var path = context.Request.Path;
var headers = context.Request.Headers;
var user = context.User.Identity?.Name;
// Write to response
context.Response.Headers.Append("X-Processed-By", "MyMiddleware");
await next(context); // forward to next middleware
});中间件注册顺序
中间件按照注册顺序运行。响应则按相反顺序展开(类似栈)。顺序至关重要:身份验证必须在授权之前运行;路由必须在端点匹配之前运行。
// Typical order for an ASP.NET Core app:
app.UseExceptionHandler("/error"); // outermost — catches everything
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("policy");
app.UseAuthentication(); // must be before Authorization
app.UseAuthorization();
app.UseRateLimiter();
app.UseOutputCache();
// MapHub, MapControllers, MapGet etc.
app.Run();Use、Run 和 Map
三种方法可以添加中间件:Use(调用下一个组件)、Run(终止管道,绝不调用下一个组件)和 Map(根据路径进行分支)。
// Use: passes to next
app.Use(async (ctx, next) =>
{
Console.WriteLine("Before");
await next(ctx);
Console.WriteLine("After");
});
// Run: terminal — no next
app.Run(async ctx =>
await ctx.Response.WriteAsync("Terminal"));
// Map: branch on path prefix
app.Map("/api", apiApp =>
apiApp.Run(async ctx =>
await ctx.Response.WriteAsync("API branch")));请求正文与响应正文
请求正文和响应正文都是流。请求正文只能读取一次——如果需要多次读取(例如在多个中间件中读取),请启用请求正文缓冲。
app.Use(async (ctx, next) =>
{
// Enable buffering so body can be read multiple times
ctx.Request.EnableBuffering();
using var reader = new StreamReader(
ctx.Request.Body,
leaveOpen: true);
var body = await reader.ReadToEndAsync();
ctx.Request.Body.Position = 0; // rewind for next middleware
Console.WriteLine($"Body: {body}");
await next(ctx);
});让管道短路
中间件可以通过写入响应且不调用 next 来让管道短路。这适用于身份验证入口、运行状况检查或维护页面。
app.Use(async (ctx, next) =>
{
if (ctx.Request.Path == "/maintenance")
{
ctx.Response.StatusCode = 503;
await ctx.Response.WriteAsync("Service under maintenance");
return; // short-circuit — next is NOT called
}
await next(ctx);
});IMiddleware 接口
对于需要 DI 的基于类的中间件,请实现 IMiddleware。在 DI 中注册该实现,然后使用 app.UseMiddleware<T>()。
public class RequestTimingMiddleware : IMiddleware
{
private readonly ILogger<RequestTimingMiddleware> _logger;
public RequestTimingMiddleware(ILogger<RequestTimingMiddleware> l) => _logger = l;
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
await next(ctx);
_logger.LogInformation("{Method} {Path}: {Ms}ms",
ctx.Request.Method, ctx.Request.Path, sw.ElapsedMilliseconds);
}
}
// Register:
builder.Services.AddTransient<RequestTimingMiddleware>();
app.UseMiddleware<RequestTimingMiddleware>();约定式中间件类
另一种方式是创建一个包含 InvokeAsync(HttpContext, RequestDelegate) 方法的类。框架会通过构造函数注入依赖项(包括下一个委托)。
public class ApiKeyMiddleware
{
private readonly RequestDelegate _next;
private readonly string _key;
// RequestDelegate injected by framework
public ApiKeyMiddleware(RequestDelegate next, IConfiguration cfg)
{
_next = next;
_key = cfg["ApiKey"] ?? "";
}
public async Task InvokeAsync(HttpContext ctx)
{
if (ctx.Request.Headers["X-Api-Key"] != _key)
{
ctx.Response.StatusCode = 401;
return;
}
await _next(ctx);
}
}调用下一个组件后修改响应
await next(ctx) 之后的代码会在响应开始写入后运行。您可以在调用 next 之前添加响应标头,但之后不能再修改状态码或正文。
app.Use(async (ctx, next) =>
{
// Before: add response headers (safe)
ctx.Response.OnStarting(() =>
{
ctx.Response.Headers.Append("X-Request-Id",
Guid.NewGuid().ToString("N"));
return Task.CompletedTask;
});
await next(ctx);
// After: response may already be sent
// SAFE: logging, metrics
// UNSAFE: changing status code or writing body
Console.WriteLine($"Response status: {ctx.Response.StatusCode}");
});实际案例:关联 ID 中间件
生产环境中的关联 ID 中间件会为每个请求添加跟踪 ID,用于分布式跟踪和日志记录。
public class CorrelationIdMiddleware
{
private readonly RequestDelegate _next;
private const string Header = "X-Correlation-Id";
public CorrelationIdMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext ctx)
{
var correlationId = ctx.Request.Headers[Header].FirstOrDefault()
?? Guid.NewGuid().ToString("N");
ctx.Items["CorrelationId"] = correlationId;
ctx.Response.OnStarting(() =>
{
ctx.Response.Headers.Append(Header, correlationId);
return Task.CompletedTask;
});
using (Serilog.Context.LogContext.PushProperty("CorrelationId", correlationId))
await _next(ctx);
}
}快速检查
添加中间件时,app.Use() 和 app.Run() 有什么区别?
回顾:ASP.NET Core 管道概览
关键要点:
- 中间件按照注册顺序运行;响应按相反顺序展开
- Use → 调用下一个组件;Run → 终止管道;Map → 基于路径进行分支
- HttpContext 包含请求、响应、用户和取消令牌
- 启用缓冲,以便多次读取请求正文
- IMiddleware 接口支持适合 DI 的基于类的中间件
- 通过 OnStarting() 添加响应标头;调用 next 后不要更改状态
用 AI 导师学习 C# — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 93
- 课程
- 346
常见问题解答
「ASP.NET Core 管道概览」课时是免费的吗?
是的 — 「ASP.NET Core 管道概览」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「ASP.NET Core 管道概览」这节课中我会学到什么?
了解请求如何流经中间件管道和 HttpContext,以及响应如何逐步构建。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「ASP.NET Core 管道概览」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- ASP.NET Core 管道概览
- 编写自定义中间件
- 短路与分支
- 中间件顺序与内置中间件