MVC中Global.asax.cs的Application_Error在IIS发布后日志异常问题
这个问题我之前也碰到过,本质是IIS发布后ASP.NET的默认错误处理逻辑和你自定义的Application_Error发生了冲突,具体原因和解决办法如下:
问题根源
当你本地调试(VS F5)时,Web.config里的<customErrors>默认是mode="RemoteOnly"或mode="Off",这时候ASP.NET不会触发内置的MVC错误处理逻辑,原始异常会直接传递到你的Application_Error方法,所以能正常记录Exception XYZ。
但发布到IIS后,默认的<customErrors>配置通常是mode="On",这时候ASP.NET MVC会自动启用内置的错误处理机制:它会在你的Application_Error执行之前先捕获异常,然后尝试自动渲染错误视图,并且默认把HandleErrorInfo作为模型传递给视图。而你的错误视图期望的是MyWebsite.Models.Services.ErrorModel,这就抛出了类型不匹配的InvalidOperationException——更糟的是,这个新异常会覆盖掉原始的Exception XYZ,导致你日志里记录的是这个类型错误,而非真正的业务异常。
哪怕你没手动注册HandleErrorAttribute,MVC在customErrors开启时也会自动触发这套逻辑,这就是问题的核心。
解决办法
要确保原始异常被正确记录,同时避免模型类型不匹配的问题,你需要完全接管错误处理流程,阻止MVC的内置逻辑介入:
1. 调整Web.config的<customErrors>配置
在Web.config的<system.web>节点下,修改配置让ASP.NET不要自动渲染错误视图,而是交给我们的Application_Error处理:
<system.web> <customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Services/Error" /> </system>
redirectMode="ResponseRewrite"会避免客户端跳转,而是直接在当前请求上下文渲染错误页,同时保证我们能捕获到原始异常。
2. 修改Application_Error方法,优先获取原始异常并手动渲染视图
原来的Response.RedirectToRoute会触发新的请求,容易让MVC的内置逻辑再次介入。我们改成直接调用控制器的Action,并传递自定义模型:
protected void Application_Error() { // 穿透HttpUnhandledException,获取真正的原始异常 Exception originalException = Server.GetLastError(); if (originalException is HttpUnhandledException httpEx) { originalException = httpEx.InnerException; } // 记录原始异常(这时候就能正确记录Exception XYZ了) _logger.Error("something bad happened", originalException); // 清除错误,阻止后续内置逻辑处理 Server.ClearError(); // 手动构建路由上下文,传递自定义模型 var routeData = new RouteData(); routeData.Values.Add("controller", "Services"); routeData.Values.Add("action", "Error"); // 把自定义模型通过路由数据传递 routeData.Values.Add("errorModel", new ErrorModel { Message = "An unexpected error occurred. Please try again later." }); // 直接调用控制器执行Action,避免跳转导致的模型替换 IController controller = new ServicesController(); controller.Execute(new RequestContext(new HttpContextWrapper(Context), routeData)); }
3. 调整ServicesController的Error方法
让它能接收我们传递的自定义模型:
public ActionResult Error(ErrorModel errorModel = null) { // 如果没有传递模型(比如极端情况),使用默认值 var model = errorModel ?? new ErrorModel{ Message = "nothing really happened go to bed" }; return View(model); }
4. 禁用MVC默认的HandleErrorAttribute
检查项目的App_Start/FilterConfig.cs文件,如果有如下代码,注释掉它,彻底禁用MVC的内置错误处理过滤器:
// filters.Add(new HandleErrorAttribute()); // 注释掉这行
验证效果
做完以上修改后,发布到IIS再访问/general视图,日志里应该能正确记录Exception XYZ,同时错误视图也能正常显示自定义的ErrorModel内容,不会再出现模型类型不匹配的异常。
内容的提问来源于stack exchange,提问作者joym8

