当前位置: 首页 > news >正文

【连载5】C# MVC 异常处理避坑指南:异步操作与静态资源错误解决方案

在 C# MVC 开发中,异常处理看似简单,实则暗藏诸多陷阱。尤其是异步操作和静态资源的错误处理,常常让开发者头疼不已。本文将揭示这些 “坑” 的真面目,并提供实用的解决方案。

坑 1:异步 Action 异常捕获问题

问题分析
异步 Action 中直接通过 Task.Run 抛出异常时,异常会被封装到 Task 对象中。若未通过 await 正确等待该任务,异常可能无法传递到调用栈上层,导致全局异常过滤器失效。

解决方案
使用 await 显式等待异步操作完成,确保异常能向上冒泡:

public async Task<ActionResult> GetData()
{await Task.Run(() => {throw new Exception("数据获取失败");});return View();
}

优化建议

  1. 避免在 Task.Run 中直接抛出异常,改为返回错误状态:
var result = await Task.Run(() => 
{if (errorCondition) return (false, null, "错误信息");return (true, data, null);
});
if (!result.success) return BadRequest(result.error);
  1. 配置全局异常过滤器(以 ASP.NET Core 为例):
services.AddControllers(options => 
{options.Filters.Add<GlobalExceptionFilter>();
});

关键点

  • async/await 会解包 Task 的异常,使其能被 try-catch 或过滤器捕获
  • 避免在异步方法中嵌套同步代码块抛出异常

坑 2:静态资源错误捕获解决方案

Web.config 配置

Web.config 文件中添加以下配置以确保静态资源错误被正确捕获和处理:

<configuration><system.webServer><modules runAllManagedModulesForAllRequests="true" /><httpErrors errorMode="Custom" existingResponse="Replace"><remove statusCode="404" subStatusCode="-1" /><error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" /><remove statusCode="500" subStatusCode="-1" /><error statusCode="500" path="/Error/ServerError" responseMode="ExecuteURL" /></httpErrors></system.webServer><system.web><customErrors mode="On" defaultRedirect="~/Error/ServerError"><error statusCode="404" redirect="~/Error/NotFound" /><error statusCode="500" redirect="~/Error/ServerError" /></customErrors></system.web>
</configuration>

Application_Error 处理

Global.asax 文件中添加以下代码,以捕获和处理静态资源错误:

protected void Application_Error(object sender, EventArgs e)
{var exception = Server.GetLastError();var httpException = exception as HttpException;Logger.Error(exception, "应用程序发生未处理异常");if (httpException != null){int statusCode = httpException.GetHttpCode();if (IsStaticResource(Request.Url.AbsolutePath)){Response.Clear();Server.ClearError();Response.TrySkipIisCustomErrors = true;Response.Redirect($"~/Error/StaticResourceError?path={Request.Url.AbsolutePath}&code={statusCode}");}}
}private bool IsStaticResource(string path)
{var staticExtensions = new[] { ".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg" };return staticExtensions.Any(ext => path.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
}

关键点说明

runAllManagedModulesForAllRequests="true" 确保所有请求都经过 ASP.NET 管道处理,包括静态资源请求。

httpErrors 配置用于 IIS 级别的错误处理,customErrors 用于 ASP.NET 级别的错误处理。

IsStaticResource 方法用于判断请求是否为静态资源,根据文件扩展名进行匹配。

坑 3:异步过滤器中的异常处理不当

在异步过滤器(如 IAsyncActionFilter)中正确处理异常至关重要,可以避免应用程序不稳定。以下是一个改进后的实现方案,确保异常被妥善处理并记录。

实现异步过滤器

public class AsyncLoggingFilter : IAsyncActionFilter
{private readonly ILogger _logger;public AsyncLoggingFilter(ILogger logger){_logger = logger;}public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){var stopwatch = Stopwatch.StartNew();try{var resultContext = await next();stopwatch.Stop();_logger.LogInformation($"Action执行完成,耗时: {stopwatch.ElapsedMilliseconds}ms");}catch (Exception ex){stopwatch.Stop();_logger.LogError(ex, $"Action执行失败,耗时: {stopwatch.ElapsedMilliseconds}ms");context.Result = new ViewResult{ViewName = "Error",ViewData = new ViewDataDictionary<HandleErrorInfo>(new HandleErrorInfo(ex, context.RouteData.Values["controller"].ToString(), context.RouteData.Values["action"].ToString()))};context.ExceptionHandled = true;}}
}

关键点说明

  1. 异常捕获:在 try-catch 块中调用 await next(),确保所有后续过滤器和 Action 的异常都能被捕获。
  2. 日志记录:使用 ILogger 记录执行时间和异常信息,便于问题排查。
  3. 异常处理
    • 设置 context.Result 返回错误视图或自定义错误响应。
    • context.ExceptionHandled 设为 true,阻止异常继续传播。
  4. 性能监控:通过 Stopwatch 记录 Action 执行时间,帮助优化性能。

注册过滤器

Startup.cs 中注册过滤器:

services.AddScoped<AsyncLoggingFilter>();
services.AddControllers(options =>
{options.Filters.Add<AsyncLoggingFilter>();
});

可选扩展

  1. 全局异常处理:如果不希望在过滤器中处理异常,可以重新抛出异常,由全局异常处理中间件捕获。
  2. 自定义错误响应:根据 API 或 MVC 需求,返回 JSON 错误响应或重定向到错误页面。

在 MVC 异常处理中,开发者常遇到一些典型问题,以下是常见场景及解决方案的整理:


异步操作中的异常处理

异步方法(如 async/await)的异常容易被忽略或未正确捕获,导致错误信息丢失。

  • 问题表现:全局异常过滤器(如 IExceptionFilter)可能无法捕获 Task 中未观察的异常。
  • 解决方案
    在异步控制器方法中显式捕获异常并通过 HttpContext 传递:
    public async Task<ActionResult> GetDataAsync() {try {var data = await _service.FetchDataAsync();return View(data);} catch (Exception ex) {HttpContext.Items["Exception"] = ex; // 传递到全局过滤器throw; // 重新抛出以触发过滤器}
    }
    
    配置 TaskScheduler.UnobservedTaskException 处理未观察的异步异常。

静态资源的错误处理

静态文件(如 CSS、JS)返回 404 或 500 错误时,默认不会触发 MVC 异常处理逻辑。

  • 问题表现web.config 中的 customErrors 或中间件可能无法覆盖静态资源请求。
  • 解决方案
    Startup.cs 中使用中间件拦截静态资源错误:
    app.UseStaticFiles(new StaticFileOptions {OnPrepareResponse = ctx => {if (ctx.Context.Response.StatusCode == 404) {ctx.Context.Response.Redirect("/Error/NotFound");}}
    });
    
    结合 IApplicationBuilder.UseExceptionHandler 统一处理异常路由。

全局异常过滤器的局限性

全局过滤器无法处理非 MVC 管道中的异常(如中间件、身份验证阶段)。

  • 解决方案
    组合使用中间件和过滤器:
    app.UseExceptionHandler(errorApp => {errorApp.Run(async context => {var exception = context.Features.Get<IExceptionHandlerFeature>();await context.Response.WriteAsync("全局错误捕获");});
    });
    
    在过滤器中补充处理控制器特定逻辑。

自定义错误页的动态传递

需在错误页中显示原始异常信息,但默认模型可能丢失细节。

  • 解决方案
    通过 ControllerContext.HttpContext.Items 传递异常数据:
    public class CustomExceptionFilter : IExceptionFilter {public void OnException(ExceptionContext context) {context.HttpContext.Items["ErrorDetail"] = context.Exception.Message;context.Result = new RedirectToRouteResult("Error", null);}
    }
    

其他常见问题

  • 跨域请求异常:需在中间件中显式处理 CORS 错误。
  • 模型绑定错误:通过 ModelState.AddModelError 捕获并返回验证信息。

开发者可根据具体场景组合上述方案。若存在其他未覆盖的问题,可进一步讨论具体案例。


文章转载自:

http://DCiYxBcn.tpLht.cn
http://ssmeIZI0.tpLht.cn
http://GVbcNmYB.tpLht.cn
http://BYtDwmIo.tpLht.cn
http://Ozu6mghG.tpLht.cn
http://IzjkdzTd.tpLht.cn
http://TqJPu1Gf.tpLht.cn
http://74soRDjY.tpLht.cn
http://CK29lfuQ.tpLht.cn
http://zZiTy3bP.tpLht.cn
http://v5JiF8hb.tpLht.cn
http://ge9hei8J.tpLht.cn
http://g7EL8LK0.tpLht.cn
http://eW6mSATM.tpLht.cn
http://TcqVpFZV.tpLht.cn
http://pfWYVgJg.tpLht.cn
http://aRCgilrY.tpLht.cn
http://zXxkURhw.tpLht.cn
http://Yc7Ijn4D.tpLht.cn
http://D3I83K1I.tpLht.cn
http://lov28pvL.tpLht.cn
http://88v7SBtN.tpLht.cn
http://a5IkPSuX.tpLht.cn
http://s6a5IjNj.tpLht.cn
http://k163o7Cq.tpLht.cn
http://fbldFOw0.tpLht.cn
http://LijgiZSu.tpLht.cn
http://tKpz0S2V.tpLht.cn
http://RJK89RhF.tpLht.cn
http://gU805Qz2.tpLht.cn
http://www.dtcms.com/a/387882.html

相关文章:

  • 当控制器无法上网时,如何利用windows笔记本与控制器共享网络?
  • 企业数字化视角下的项目管理软件市场全景分析(2025版)
  • Python异步编程:asyncio.create_task() 用法解析
  • java面试Day1 | redis缓存穿透、击穿、雪崩、持久化、双写一致性、数据过期策略、数据淘汰策略、分布式锁、redis集群
  • Jenkins运维之路(容器项目的优化)
  • 【精品资料鉴赏】363页智慧旅游大数据平台项目建设设计方案
  • 软考 系统架构设计师系列知识点之杂项集萃(149)
  • MyBatis 中注解操作与 XML 映射文件操作的对比
  • 复杂 PDF 文档如何高效解析?
  • 加密网络流量分类
  • leetcode算法题记录:
  • VS安装后通过vswhere.exe查询显示的 installationVersion数字怎么不是2022?
  • 光伏电站安全 “守护神”:QB800 绝缘监测平台,为清洁能源高效运行筑固防线
  • STC携手非小号 Talking Web3,海上ALPHA WEB3派对启航
  • AR技术突破:极端环境下设备的创新与应用
  • R---------split()` 函数
  • 和为K的子数组-前缀和+哈希
  • ITSM产品推荐:甄知科技燕千云与主流方案对比分析
  • 线性回归与 Softmax 回归核心知识点总结
  • OpenLayers数据源集成 -- 章节十八:GML图层详解:OGC标准地理标记语言的完整集成与智能样式渲染方案
  • 线性回归与 Softmax 回归核心内容总结
  • 【数据分享】各省农业新质生产力数据(2012-2023)
  • 整理SpringBoot实现文件上传所需的知识
  • Cesium 加载ArcGIS 地图源到国内地图源的切换
  • 2010/12 JLPT听力原文 问题四
  • html页面转PDF
  • day3 MySOL多表操作
  • 触觉智能RK3576开发板OpenHarmony开源鸿蒙系统USB控制传输功能示例
  • 阿里云开源通义 DeepResearch!轻量级 AI 代理性能对标 OpenAI,系统性技术创新赋能研究能力​
  • WSL Git Clone 项目识别 `.git` 问题记录