c# 如何在ASP.NET Core中间件中使用异步

来源:这里教程网 时间:2026-02-21 17:40:44 作者:

ASP.NET Core中间件中必须用
async
+
await
,不能只写
async
方法但同步调用

中间件委托签名是

Func<httpcontext requestdelegate task></httpcontext>
,返回类型必须是
Task
。如果方法声明为
async Task
却没用
await
,编译器会警告,运行时可能阻塞线程或丢弃未等待的
Task

错误写法:
app.Use(async (context, next) =>
{
    SomeAsyncOperation(); // 忘了 await → 返回 void,中间件立即往下走
    await next();
});
正确写法:
app.Use(async (context, next) =>
{
    await SomeAsyncOperation(); // 真正 await
    await next();
});
若异步操作无依赖后续逻辑,可用
ConfigureAwait(false)
避免上下文捕获(尤其在非 UI 场景)

next()
本身必须
await
,否则请求流程中断

next()
是下一个中间件的入口,它返回
Task
。不
await
它会导致当前中间件“假性完成”,后续中间件可能没执行,响应也可能提前结束或空内容。

典型现象:HTTP 200 状态码返回,但响应体为空,或日志显示
next()
后的代码已执行,但浏览器收不到数据
即使你只做日志或监控,也得
await next()
,否则请求生命周期被截断
想在下游执行完后补逻辑?把代码放在
await next()
后面即可:
app.Use(async (context, next) =>
{
    var sw = Stopwatch.StartNew();
    await next(); // 等下游全部跑完
    sw.Stop();
    _logger.LogInformation("Total time: {Elapsed}", sw.ElapsedMilliseconds);
});

避免在中间件里用
Task.Run
包装同步 I/O 操作

有人误以为“加个

Task.Run
就是异步”,但在 ASP.NET Core 中,这反而增加线程调度开销,还可能耗尽线程池。真正的异步应来自底层支持(如
Stream.ReadAsync
HttpClient.GetAsync
、EF Core 的
ToListAsync
)。

反模式:
app.Use(async (context, next) =>
{
    await Task.Run(() => {
        var data = File.ReadAllText("config.json"); // 同步读文件 → 假异步
    });
    await next();
});
正解:改用真正异步 API:
await using var stream = File.OpenRead("config.json");
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
注意:
File.ReadAllTextAsync
在 .NET 6+ 才有;旧版本需手动构造
FileStream
+
StreamReader

异常处理要覆盖整个
await
链,别漏掉
next()

中间件里

try/catch
只包自己代码,不包
await next()
,那下游抛的异常就逃逸出去了,可能触发全局 500 且没日志。

常见疏忽:
try
{
    await DoSomethingAsync();
    // 忘了把 await next() 放进 try 块!
}
catch (Exception ex)
{
    _logger.LogError(ex, "Failed in middleware");
}
安全写法:
try
{
    await DoSomethingAsync();
    await next(); // 这句也要在 try 内
}
catch (Exception ex)
{
    _logger.LogError(ex, "Middleware failed");
    throw; // 或写响应,但别静默吞掉
}
更推荐用全局异常处理中间件(
UseExceptionHandler
),但自定义中间件内仍需确保
await next()
不裸奔
中间件异步的核心就一条:所有
Task
返回值的操作,只要语义上需要等它完成,就必须
await
——包括你自己的调用,也包括
next()
。漏掉任何一个,都可能让请求流断裂、资源泄漏或行为不可预测。

相关推荐