核心警示:
你添加 try-catch 本想保護應用,
結果卻:隱藏錯誤、觸發重試風暴、讓故障追蹤難如登天。
在 .NET 中,try-catch 并非總是盟友——有時它正是系統無聲崩潰的元兇。
正確處理異常的關鍵:讓應用高聲報錯、優雅恢復、永不讓你猜謎!
?? 典型災難代碼
try
{
var user = await _userService.GetUserAsync(id);
_logger.LogInformation("Fetched user");
}
catch (Exception ex) // 全類型捕獲
{
_logger.LogError(ex, "Something went wrong");
}
? 看似安全
? 靜默掩蓋根因
?? 違反關注點分離
?? 引發重試循環、丟失指標、調試地獄
? 五大 try-catch 反模式(附解決方案)
? 修復方案 1:使用 when
過濾器
try
{
// 業務邏輯
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
_logger.LogWarning("用戶不存在"); // 精準捕獲
}
優勢:
?? 避免無關異常誤入
?? 保持處理邏輯精準可控
?? 重要提示:僅捕獲可處理的異常。未知庫故障等極少數場景下,可用寬泛捕獲記錄日志后重新拋出(throw;
)
? 修復方案 2:預防代替捕獲
if (!File.Exists(path)) // 防御性檢查
{
_logger.LogWarning("文件缺失: {path}", path);
return;
}
// 而非盲目捕獲 FileNotFoundException
原則:
?? 防御性編程 > 異常控制流
?? 根據場景合理選用,非萬能方案
? 修復方案 3:創建領域專屬異常
public classPaymentDeclinedException : Exception// 自定義異常
{
public PaymentDeclinedException(string reason) : base(reason) { }
}
// 精準捕獲可處理的異常
try
{
await _paymentService.ProcessAsync();
}
catch (PaymentDeclinedException ex) // 僅捕獲支付拒絕
{
_logger.LogWarning("支付拒絕: {reason}", ex.Message);
return BadRequest(ex.Message);
}
價值:
?? 提升代碼可讀性
?? 增強可測試性
?? 明確恢復路徑
? 修復方案 4:中間件統一處理
// 注冊異常處理中間件
app.UseExceptionHandler("/error");
// 集中處理錯誤響應
app.Map("/error", (HttpContext context) =>
{
var feature = context.Features.Get<IExceptionHandlerFeature>();
return Results.Problem(detail: feature?.Error.Message);
});
優勢:
?? 全局統一錯誤響應
?? 業務代碼零污染
? 修復方案 5:后臺任務禁止吞沒異常
try
{
await _processor.RunAsync();
}
catch (Exception ex)
{
_logger.LogCritical(ex, "任務失敗");
throw; // 重新拋出!讓編排器(如Azure Functions)可見故障
}
關鍵原則:
?? 后臺任務靜默失敗 = 定時炸彈
?? 必須拋出以便觸發重試/告警
? 修復方案 6:分層彈性策略(Polly)
services.AddHttpClient("Users")
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, retry => TimeSpan.FromSeconds(Math.Pow(2, retry)))); // 指數退避重試
核心價值:
?? HTTP層處理瞬時故障
?? 業務代碼無侵入
?? 專家級技巧:生產就緒清單
?? 終極忠告
try-catch 不是安全網,而是精密手術刀
我親歷的 .NET 生產事故中,從未因未捕獲異常引發宕機,
真正的災難總是:
?? 異常被捕獲 → 記錄日志 → 靜默忽略 → 用戶發現故障時已無力回天
沒有告警,沒有重試,只有沉默的崩潰。
讓錯誤暴露在陽光下,才是真正的韌性。
該文章在 2025/7/24 14:53:37 編輯過