在c#应用开发过程中,未处理的异常可能会导致程序直接崩溃,影响用户体验和系统稳定性,全局异常处理能够统一捕获应用运行过程中出现的未处理异常,进行统一的日志记录、友好提示或者错误上报,是开发中非常实用的功能。
c#全局异常处理的常见实现方式
1. 控制台应用全局异常处理
控制台应用可以通过订阅AppDomain.CurrentDomain.UnhandledException事件来捕获未处理的异常,该事件会在应用域中出现未处理异常时触发。
using System;
using System.Threading;
class Program
{
static void Main(string[] args)
{
// 订阅未处理异常事件
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
// 模拟触发异常
throw new InvalidOperationException("控制台应用测试异常");
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
Console.WriteLine($"捕获到未处理异常:{ex?.Message}");
// 记录日志逻辑可在此处添加
// 如果是终止状态的异常,可在此处做收尾工作
if (e.IsTerminating)
{
Console.WriteLine("应用即将终止");
}
}
}
2. Windows窗体应用全局异常处理
Windows窗体应用除了可以订阅AppDomain.CurrentDomain.UnhandledException事件外,还可以订阅Application.ThreadException事件来捕获UI线程的未处理异常,同时需要设置Application.SetUnhandledExceptionMode来指定异常的处理模式。
using System;
using System.Windows.Forms;
namespace WinFormsApp
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 设置未处理异常模式为捕获所有异常
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// 订阅UI线程异常事件
Application.ThreadException += Application_ThreadException;
// 订阅应用域未处理异常事件
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.Run(new Form1());
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
MessageBox.Show($"UI线程捕获到异常:{e.Exception.Message}", "错误提示");
// 记录日志
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
MessageBox.Show($"应用域捕获到未处理异常:{ex?.Message}", "错误提示");
// 记录日志
}
}
}
3. ASP.NET Core应用全局异常处理
ASP.NET Core中通常使用中间件来实现全局异常处理,通过自定义中间件可以拦截所有请求处理过程中出现的未处理异常,返回统一的错误响应。
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Text.Json;
using System.Threading.Tasks;
namespace WebApp
{
// 自定义全局异常处理中间件
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
public GlobalExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
// 调用下一个中间件
await _next(context);
}
catch (Exception ex)
{
// 处理异常
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
var response = new
{
code = 500,
message = "服务器内部错误,请稍后重试",
detail = ex.Message // 生产环境可去掉detail,避免暴露敏感信息
};
var jsonResponse = JsonSerializer.Serialize(response);
await context.Response.WriteAsync(jsonResponse);
// 记录异常日志
}
}
// 中间件扩展方法,方便注册
public static class GlobalExceptionMiddlewareExtensions
{
public static IApplicationBuilder UseGlobalException(this IApplicationBuilder builder)
{
return builder.UseMiddleware<GlobalExceptionMiddleware>();
}
}
}
在Startup.cs或者Program.cs中注册该中间件,注意要放在其他中间件之前,确保能够捕获所有请求的异常:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// 注册全局异常处理中间件,放在最前面
app.UseGlobalException();
app.MapGet("/", () => "Hello World!");
app.Run();
c#全局异常处理常见问题与排错方法
问题1:全局异常处理配置后不生效
可能原因:
- ASP.NET Core中间件注册顺序错误,全局异常处理中间件没有放在其他中间件之前,导致部分中间件的异常无法被捕获。
- 控制台应用或者窗体应用没有正确订阅对应的异常事件,或者事件订阅代码放在了可能触发异常的代码之后。
- 异常是在子线程中抛出的,没有订阅子线程的异常处理事件,比如
Task中未等待的异常。
排错方法:
- 检查ASP.NET Core中间件注册顺序,确保全局异常处理中间件是第一个被注册的。
- 确认异常事件订阅代码在应用启动的最初阶段执行,早于所有可能触发异常的业务代码。
- 对于异步任务中的异常,使用
try-catch包裹,或者订阅TaskScheduler.UnobservedTaskException事件捕获未观察到的任务异常。
问题2:异常信息不完整,无法定位问题
可能原因:
- 捕获异常时只记录了
Message属性,没有记录StackTrace、InnerException等关键信息。 - 日志组件配置错误,导致异常信息没有被正确写入日志文件或者日志系统。
排错方法:
- 在记录异常时,除了
Message,还要记录StackTrace和递归获取InnerException的信息,示例代码如下:
private static string GetFullExceptionMessage(Exception ex)
{
if (ex == null) return string.Empty;
var message = $"异常消息:{ex.Message},堆栈跟踪:{ex.StackTrace}";
if (ex.InnerException != null)
{
message += $",内部异常:{GetFullExceptionMessage(ex.InnerException)}";
}
return message;
}
- 检查日志组件的配置,确认日志级别、输出路径、输出格式是否正确,手动触发一个测试异常验证日志是否能够正常输出。
问题3:部分场景的异常无法被全局异常处理捕获
可能原因:
- ASP.NET Core中,部分中间件(比如静态文件中间件)的异常可能在自定义全局异常处理中间件之前抛出,或者异常被其他中间件提前处理了。
- 应用中有主动
catch异常但是没有重新抛出,导致异常被吞掉,无法到达全局异常处理逻辑。 - 在ASP.NET Core中使用了
ExceptionFilter,过滤器的异常处理逻辑覆盖了中间件的全局异常处理。
排错方法:
- 调整中间件顺序,将全局异常处理中间件放在尽可能靠前的位置,同时检查是否有其他中间件提前处理了异常。
- 检查业务代码中的
try-catch块,确保如果需要全局处理异常,不要在catch中吞掉异常,或者只在catch中做记录后重新抛出。 - 如果同时使用了
ExceptionFilter和全局异常处理中间件,明确两者的职责,避免重复处理,或者调整执行顺序,确保异常能够被预期的逻辑处理。
全局异常处理的注意事项
- 生产环境中不要将异常的详细堆栈信息直接返回给客户端,避免暴露系统内部实现细节,带来安全风险。
- 全局异常处理逻辑本身也要尽可能避免抛出异常,否则会导致应用进入不可控状态。
- 对于不同类型的异常,可以根据业务需求做差异化的处理,比如业务异常返回友好的提示,系统异常记录详细日志并返回通用错误提示。
- 定期查看全局异常处理记录的日志,分析异常出现的频率和原因,优化代码减少异常的出现。
C#全局异常处理ASP.NET_Core异常捕获中间件修改时间:2026-06-18 16:27:51