【连载7】 C# MVC 跨框架异常处理对比:.NET Framework 与 .NET Core 实现差异

异常处理是任何应用程序的关键部分,尤其在 MVC 架构中,统一的异常处理机制能极大提升代码可维护性。下面通过代码示例对比 .NET Framework 与 .NET Core 在 MVC 异常处理上的实现差异

1. 全局异常处理实现

迁移到 .NET Core 的异常处理挑战

从 .NET Framework 迁移到 .NET Core 时,异常处理机制的差异可能带来以下挑战:

HTTP 管道差异

在 .NET Framework 中,Global.asaxHttpApplicationApplication_Error 是全局异常处理的主要方式。而 .NET Core 采用中间件(Middleware)模式,需使用 UseExceptionHandler 或自定义中间件捕获异常,迁移时需调整代码结构。

异步代码支持

.NET Core 更强调异步编程,未处理的异步异常可能导致进程崩溃。需确保 async/await 方法中异常被正确捕获,或通过 TaskScheduler.UnobservedTaskException 处理未观察的任务异常。

依赖注入集成

.NET Core 内置 DI 容器,异常处理中间件可能依赖注入的服务。需在中间件中正确解析服务,例如通过构造函数注入或 HttpContext.RequestServices

日志记录变化

.NET Framework 中常用 System.Diagnostics.Trace 或第三方日志库,而 .NET Core 推荐使用 ILogger 接口。迁移时需重构日志记录逻辑,确保异常信息被正确输出到控制台、文件或第三方平台(如 Serilog)。

其他关键差异点

Kestrel 与 IIS 集成

.NET Core 默认使用 Kestrel 作为 Web 服务器,异常处理行为可能与 IIS 不同。例如,Kestrel 对请求超时或连接中断的响应方式需特别配置。

配置文件转换
web.configappsettings.json 替代,异常处理相关的配置(如自定义错误页面路径)需迁移到新格式,并通过代码加载。

跨平台兼容性

.NET Core 运行在 Linux/macOS 时,某些 Windows 特定的异常(如 COM 组件调用)可能不适用,需替换为跨平台方案或条件编译。

欢迎在评论区补充你的迁移经验,例如如何解决特定异常类型兼容性问题,或优化中间件性能的实践!

.NET Framework 实现方式

在 .NET Framework 中,全局异常处理通常通过自定义 HandleErrorAttribute 和全局过滤器实现。以下是实现步骤:

创建自定义异常处理特性类 CustomHandleErrorAttribute,继承自 HandleErrorAttribute

csharp 复制代码
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled)
            return;
            
        var exception = filterContext.Exception;
        LogException(exception);
        
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new JsonResult
            {
                Data = new { success = false, error = exception.Message },
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            filterContext.Result = new ViewResult
            {
                ViewName = "Error",
                ViewData = new ViewDataDictionary(exception)
            };
        }
        
        filterContext.ExceptionHandled = true;
    }
    
    private void LogException(Exception ex)
    {
        // 日志记录逻辑
    }
}

FilterConfig 类中注册全局过滤器:

csharp 复制代码
public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
    }
}

Global.asaxApplication_Start 方法中调用过滤器注册:

csharp 复制代码
protected void Application_Start()
{
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}

.NET Core 实现方式

在 .NET Core 中,全局异常处理通常通过自定义中间件实现。以下是实现步骤:

创建异常处理中间件类 ExceptionMiddleware

csharp 复制代码
public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionMiddleware> _logger;

    public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            await _next(httpContext);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "发生未处理的异常");
            await HandleExceptionAsync(httpContext, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        var response = new
        {
            statusCode = context.Response.StatusCode,
            message = "发生内部服务器错误",
            detailed = exception.Message
        };

        var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        var json = JsonSerializer.Serialize(response, options);

        await context.Response.WriteAsync(json);
    }
}

Program.cs 中配置中间件:

csharp 复制代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseMiddleware<ExceptionMiddleware>();
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

日志记录建议

在异常处理中,日志记录是重要环节。建议使用现有的日志框架:

在 .NET Framework 中使用 NLog:

csharp 复制代码
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

private void LogException(Exception ex)
{
    Logger.Error(ex, "发生未处理的异常");
}

在 .NET Core 中使用内置的 ILogger:

csharp 复制代码
_logger.LogError(ex, "发生未处理的异常");

生产环境注意事项

对于生产环境,应避免向客户端返回详细的异常信息。可以修改中间件以返回通用错误信息:

csharp 复制代码
var response = new
{
    statusCode = context.Response.StatusCode,
    message = "发生内部服务器错误",
    detailed = app.Environment.IsDevelopment() ? exception.Message : null
};

2. 常见的 "坑" 及解决方案

坑 1:异常处理范围不完整

在.NET Core中,全局异常处理可以通过中间件实现,覆盖整个请求管道的异常,包括路由、身份验证等阶段。以下为具体方法:

自定义异常处理中间件

创建中间件捕获所有异常并统一处理:

csharp 复制代码
public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    public ExceptionMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        try { await _next(context); }
        catch (Exception ex)
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            await context.Response.WriteAsync("全局异常处理: " + ex.Message);
        }
    }
}

注册到Startup.cs

csharp 复制代码
app.UseMiddleware<ExceptionMiddleware>();

内置异常处理中间件

直接使用.NET Core提供的UseExceptionHandler

csharp 复制代码
app.UseExceptionHandler("/Error"); // 指定错误路由
app.UseStatusCodePagesWithReExecute("/Error/{0}"); // 处理404等状态码

对比旧版Framework方案

相比传统ASP.NET MVC的HandleErrorAttribute,中间件方案的优势在于:

  • 可处理中间件管道中任何阶段的异常
  • 能捕获静态文件处理、身份验证等非控制器逻辑的异常
  • 支持自定义响应格式(JSON/HTML等)

进阶建议

  • 生产环境区分开发/生产错误页(UseDeveloperExceptionPage
  • 结合日志系统(如Serilog)记录异常上下文
  • 对API项目可返回标准化错误JSON格式

坑 2:开发 / 生产环境配置混淆

在开发环境中需要显示详细错误信息以便于调试,但在生产环境中需要隐藏这些信息以避免安全隐患。使用条件判断确保不同环境采用不同的异常处理策略。

复制代码
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseMiddleware<ExceptionMiddleware>();
    app.UseHsts();
}

坑 3:异常被多次处理

在 .NET Framework 中,同时使用 HandleErrorAttributeApplication_Error 事件可能导致异常被重复处理。应选择一种全局异常处理机制,并正确设置 ExceptionHandled = true 以避免重复处理。

坑 4:异步代码中的异常丢失

早期版本的 .NET Framework 在处理异步控制器方法时可能存在异常捕获不完整的问题。升级至最新版本或迁移到 .NET Core 可以解决这一问题,后者对异步代码的异常处理机制更加完善。

坑 5:日志记录不完整

仅记录异常消息而不记录堆栈跟踪或请求信息会导致调试困难。应采用完整的日志记录方式,包含异常对象、请求方法和路径等关键信息。

复制代码
_logger.LogError(ex, 
    "异常发生在请求 {Method} {Path}", 
    context.Request.Method, 
    context.Request.Path);

3.讨论

迁移到 .NET Core 的异常处理挑战

从 .NET Framework 迁移到 .NET Core 时,异常处理机制的差异可能带来以下挑战:

HTTP 管道差异

在 .NET Framework 中,Global.asaxHttpApplicationApplication_Error 是全局异常处理的主要方式。而 .NET Core 采用中间件(Middleware)模式,需使用 UseExceptionHandler 或自定义中间件捕获异常,迁移时需调整代码结构。

异步代码支持

.NET Core 更强调异步编程,未处理的异步异常可能导致进程崩溃。需确保 async/await 方法中异常被正确捕获,或通过 TaskScheduler.UnobservedTaskException 处理未观察的任务异常。

依赖注入集成

.NET Core 内置 DI 容器,异常处理中间件可能依赖注入的服务。需在中间件中正确解析服务,例如通过构造函数注入或 HttpContext.RequestServices

日志记录变化

.NET Framework 中常用 System.Diagnostics.Trace 或第三方日志库,而 .NET Core 推荐使用 ILogger 接口。迁移时需重构日志记录逻辑,确保异常信息被正确输出到控制台、文件或第三方平台(如 Serilog)。

其他关键差异点

Kestrel 与 IIS 集成

.NET Core 默认使用 Kestrel 作为 Web 服务器,异常处理行为可能与 IIS 不同。例如,Kestrel 对请求超时或连接中断的响应方式需特别配置。

配置文件转换
web.configappsettings.json 替代,异常处理相关的配置(如自定义错误页面路径)需迁移到新格式,并通过代码加载。

跨平台兼容性

.NET Core 运行在 Linux/macOS 时,某些 Windows 特定的异常(如 COM 组件调用)可能不适用,需替换为跨平台方案或条件编译。

欢迎在评论区补充你的迁移经验,例如如何解决特定异常类型兼容性问题,或优化中间件性能的实践!

相关推荐
ajassi20003 小时前
开源 C# 快速开发(五)自定义控件--仪表盘
开发语言·开源·c#
玩泥巴的3 小时前
.NET操作Word实现智能文档处理 - 内容查找替换与书签操作
word·.net·二次开发·office·com互操作
Dream achiever4 小时前
3.WPF - 依赖属性
c#·wpf
mudtools4 小时前
.NET操作Excel:高级格式设置
c#·.net·excel·wps
温启志c#5 小时前
winform c# 做的2个运控平台,通过修改表格 的方式,也可以通过语音识别的交互方式,更加智能。
开发语言·c#
Dream achiever6 小时前
11.WPF 的命令处理事件--参数介绍
开发语言·c#·wpf
布伦鸽7 小时前
C# WPF DataGrid 数据绑定时的单元格编辑类型模板
开发语言·c#·wpf
入目星河滚烫7 小时前
Unity避坑——继承了MonoBehaviour的对象不能通过new来创建
unity·c#·游戏引擎
大飞pkz8 小时前
【设计模式】组合模式
开发语言·设计模式·c#·组合模式