ASP.NET MVC 自定义错误处理 Application_Error Global.asax?
Posted
技术标签:
【中文标题】ASP.NET MVC 自定义错误处理 Application_Error Global.asax?【英文标题】:ASP.NET MVC Custom Error Handling Application_Error Global.asax? 【发布时间】:2010-11-13 08:24:46 【问题描述】:我有一些基本代码来确定我的 MVC 应用程序中的错误。目前在我的项目中,我有一个名为Error
的控制器,其操作方法为HTTPError404()
、HTTPError500()
和General()
。它们都接受字符串参数error
。使用或修改下面的代码。
将数据传递给错误控制器进行处理的最佳/正确方法是什么?我希望有一个尽可能强大的解决方案。
protected void Application_Error(object sender, EventArgs e)
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
if (httpException != null)
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
switch (httpException.GetHttpCode())
case 404:
// page not found
routeData.Values.Add("action", "HttpError404");
break;
case 500:
// server error
routeData.Values.Add("action", "HttpError500");
break;
default:
routeData.Values.Add("action", "General");
break;
routeData.Values.Add("error", exception);
// clear error on server
Server.ClearError();
// at this point how to properly pass route data to error controller?
【问题讨论】:
【参考方案1】:您可以重定向到您的控制器/操作并通过查询字符串传递信息,而不是为此创建新路由。例如:
protected void Application_Error(object sender, EventArgs e)
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
if (httpException != null)
string action;
switch (httpException.GetHttpCode())
case 404:
// page not found
action = "HttpError404";
break;
case 500:
// server error
action = "HttpError500";
break;
default:
action = "General";
break;
// clear error on server
Server.ClearError();
Response.Redirect(String.Format("~/Error/0/?message=1", action, exception.Message));
然后你的控制器会收到你想要的任何东西:
// GET: /Error/HttpError404
public ActionResult HttpError404(string message)
return View("SomeView", message);
您的方法需要权衡取舍。在这种错误处理中循环时要非常小心。另一件事是,由于您正在通过 asp.net 管道处理 404,因此您将为所有这些命中创建一个会话对象。对于频繁使用的系统,这可能是一个问题(性能)。
【讨论】:
当你说“小心循环”时,你到底是什么意思?有没有更好的方法来处理这种类型的错误重定向(假设它是一个频繁使用的系统)? 循环我的意思是当你的错误页面中有错误时,你会一次又一次地被重定向到你的错误页面......(例如,你想在数据库中记录你的错误并且它已关闭)。 错误重定向违背了网络架构。当服务器响应正确的 HTTP 状态代码时,URI 应该保持不变,以便客户端知道失败的确切上下文。实现 HandleErrorAttribute.OnException 或 Controller.OnException 是更好的解决方案。如果失败,请在 Global.asax 中执行 Server.Transfer("~/Error")。 @Chris,这是可以接受的,但不是最佳实践。尤其是因为它经常被重定向到一个带有 HTTP 200 状态代码的资源文件,这让客户端相信一切正常。 我必须将回答最初的问题“如何正确地将路由数据传递给错误控制器?”:
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
然后在您的 ErrorController 类中,实现如下函数:
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Error(Exception exception)
return View("Error", exception);
这会将异常推送到视图中。视图页面应声明如下:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<System.Exception>" %>
以及显示错误的代码:
<% if(Model != null) %> <p><b>Detailed error:</b><br /> <span class="error"><%= Helpers.General.GetErrorMessage((Exception)Model, false) %></span></p> <% %>
这是从异常树中收集所有异常消息的函数:
public static string GetErrorMessage(Exception ex, bool includeStackTrace)
StringBuilder msg = new StringBuilder();
BuildErrorMessage(ex, ref msg);
if (includeStackTrace)
msg.Append("\n");
msg.Append(ex.StackTrace);
return msg.ToString();
private static void BuildErrorMessage(Exception ex, ref StringBuilder msg)
if (ex != null)
msg.Append(ex.Message);
msg.Append("\n");
if (ex.InnerException != null)
BuildErrorMessage(ex.InnerException, ref msg);
【讨论】:
【参考方案3】:我找到了 Lion_cl 指出的 ajax 问题的解决方案。
global.asax:
protected void Application_Error()
if (HttpContext.Current.Request.IsAjaxRequest())
HttpContext ctx = HttpContext.Current;
ctx.Response.Clear();
RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
rc.RouteData.Values["action"] = "AjaxGlobalError";
// TODO: distinguish between 404 and other errors if needed
rc.RouteData.Values["newActionName"] = "WrongRequest";
rc.RouteData.Values["controller"] = "ErrorPages";
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
IController controller = factory.CreateController(rc, "ErrorPages");
controller.Execute(rc);
ctx.Server.ClearError();
错误页面控制器
public ActionResult AjaxGlobalError(string newActionName)
return new AjaxRedirectResult(Url.Action(newActionName), this.ControllerContext);
AjaxRedirectResult
public class AjaxRedirectResult : RedirectResult
public AjaxRedirectResult(string url, ControllerContext controllerContext)
: base(url)
ExecuteResult(controllerContext);
public override void ExecuteResult(ControllerContext context)
if (context.RequestContext.HttpContext.Request.IsAjaxRequest())
javascriptResult result = new JavaScriptResult()
Script = "tryhistory.pushState(null,null,window.location.href);catch(err)window.location.replace('" + UrlHelper.GenerateContentUrl(this.Url, context.HttpContext) + "');"
;
result.ExecuteResult(context);
else
base.ExecuteResult(context);
AjaxRequestExtension
public static class AjaxRequestExtension
public static bool IsAjaxRequest(this HttpRequest request)
return (request.Headers["X-Requested-With"] != null && request.Headers["X-Requested-With"] == "XMLHttpRequest");
【讨论】:
在实现这一点时,我收到以下错误:“System.Web.HttpRequest”不包含“IsAjaxRequest”的定义。本文有解决办法:***.com/questions/14629304/…【参考方案4】:之前,我曾为在 MVC 应用程序中集中一个全局错误处理例程的想法而苦恼。我有一个post on the ASP.NET forums。
它基本上可以处理 global.asax 中的所有应用程序错误,而无需错误控制器、使用 [HandlerError]
属性进行装饰或在 web.config 中摆弄 customErrors
节点。
【讨论】:
【参考方案5】:也许在 MVC 中处理错误的更好方法是将 HandleError 属性应用于您的控制器或操作并更新 Shared/Error.aspx 文件以执行您想要的操作。该页面上的 Model 对象包括一个 Exception 属性以及 ControllerName 和 ActionName。
【讨论】:
那么您将如何处理404
错误?因为没有为此指定控制器/操作?
接受的答案包括 404。这种方法只对 500 个错误有用。
也许您应该将其编辑到您的答案中。 Perhaps a better way of handling errors
听起来很像 All Errors 而不仅仅是 500。【参考方案6】:
这可能不是 MVC 的最佳方式 (https://***.com/a/9461386/5869805)
以下是如何在 Application_Error 中呈现视图并将其写入 http 响应。您不需要使用重定向。这将阻止对服务器的第二次请求,因此浏览器地址栏中的链接将保持不变。这可能是好是坏,这取决于你想要什么。
Global.asax.cs
protected void Application_Error()
var exception = Server.GetLastError();
// TODO do whatever you want with exception, such as logging, set errorMessage, etc.
var errorMessage = "SOME FRIENDLY MESSAGE";
// TODO: UPDATE BELOW FOUR PARAMETERS ACCORDING TO YOUR ERROR HANDLING ACTION
var errorArea = "AREA";
var errorController = "CONTROLLER";
var errorAction = "ACTION";
var pathToViewFile = $"~/Areas/errorArea/Views/errorController/errorAction.cshtml"; // THIS SHOULD BE THE PATH IN FILESYSTEM RELATIVE TO WHERE YOUR CSPROJ FILE IS!
var requestControllerName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["controller"]);
var requestActionName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["action"]);
var controller = new BaseController(); // REPLACE THIS WITH YOUR BASE CONTROLLER CLASS
var routeData = new RouteData DataTokens = "area", errorArea , Values = "controller", errorController , "action", errorAction ;
var controllerContext = new ControllerContext(new HttpContextWrapper(HttpContext.Current), routeData, controller);
controller.ControllerContext = controllerContext;
var sw = new StringWriter();
var razorView = new RazorView(controller.ControllerContext, pathToViewFile, "", false, null);
var model = new ViewDataDictionary(new HandleErrorInfo(exception, requestControllerName, requestActionName));
var viewContext = new ViewContext(controller.ControllerContext, razorView, model, new TempDataDictionary(), sw);
viewContext.ViewBag.ErrorMessage = errorMessage;
//TODO: add to ViewBag what you need
razorView.Render(viewContext, sw);
HttpContext.Current.Response.Write(sw);
Server.ClearError();
HttpContext.Current.Response.End(); // No more processing needed (ex: by default controller/action routing), flush the response out and raise EndRequest event.
查看
@model HandleErrorInfo
@
ViewBag.Title = "Error";
// TODO: SET YOUR LAYOUT
<div class="">
ViewBag.ErrorMessage
</div>
@if(Model != null && HttpContext.Current.IsDebuggingEnabled)
<div class="" style="background:khaki">
<p>
<b>Exception:</b> @Model.Exception.Message <br/>
<b>Controller:</b> @Model.ControllerName <br/>
<b>Action:</b> @Model.ActionName <br/>
</p>
<div>
<pre>
@Model.Exception.StackTrace
</pre>
</div>
</div>
【讨论】:
这是 IMO 的最佳方式。正是我想要的。 @SteveHarris 很高兴它有帮助! :) 我正在使用这种方法,但是一些错误没有显示在错误页面中,但它也进入了错误页面。 我会将断点放在 Application_Error 方法的开头和视图代码中以“@if(”开头的行,然后检查 (i) 执行是否真的命中 Application_Error 方法,(ii)视图代码中的模型不为空,并且包含异常对象,并且 (iii) 启用了调试。顺便说一句,据我所知,视图上的断点仅在视图代码自上次编译后未更改时才有效。【参考方案7】:Application_Error 与 Ajax 请求有关。如果在 Ajax 调用的 Action 中处理了错误 - 它会在结果容器中显示您的错误视图。
【讨论】:
【参考方案8】:布赖恩, 这种方法适用于非 Ajax 请求,但正如 Lion_cl 所说,如果您在 Ajax 调用期间出现错误,您的 Share/Error.aspx 视图(或您的自定义错误页面视图)将返回给 Ajax 调用者——用户不会被重定向到错误页面。
【讨论】:
【参考方案9】:使用以下代码在路由页面上进行重定向。 在异常中使用 exception.Message。 Coz 异常查询字符串如果扩展查询字符串长度会给出错误。
routeData.Values.Add("error", exception.Message);
// clear error on server
Server.ClearError();
Response.RedirectToRoute(routeData.Values);
【讨论】:
【参考方案10】:我对这种错误处理方法有疑问: 如果是 web.config:
<customErrors mode="On"/>
错误处理程序正在搜索视图 Error.shtml 只有在异常发生后,控制流才会进入 Application_Error global.asax
System.InvalidOperationException:视图“错误”或其主视图是 未找到或没有视图引擎支持搜索的位置。这 搜索了以下位置:~/Views/home/Error.aspx ~/Views/home/Error.ascx ~/Views/Shared/Error.aspx ~/Views/Shared/Error.ascx ~/Views/home/Error.cshtml ~/Views/home/Error.vbhtml ~/Views/Shared/Error.cshtml ~/Views/Shared/Error.vbhtml 在 System.Web.Mvc.ViewResult.FindView(ControllerContext 上下文) .....................
所以
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
httpException 总是 null 然后
customErrors 模式=“开”
:(
这是误导
然后<customErrors mode="Off"/>
或<customErrors mode="RemoteOnly"/>
用户看到customErrors html,
那么customErrors mode="On" 这个代码也是错误的
这段代码的另一个问题
Response.Redirect(String.Format("~/Error/0/?message=1", action, exception.Message));
返回代码为 302 的页面,而不是真正的错误代码(402,403 等)
【讨论】:
以上是关于ASP.NET MVC 自定义错误处理 Application_Error Global.asax?的主要内容,如果未能解决你的问题,请参考以下文章