ASP.NET MVC 自定义错误页面心得
自定义错误页面的目的,就是为了能让程序在出现错误/异常的时候,能够有较好的显示体验。
所以,首先要先了解,我们可以在哪里捕获异常。
当程序发生错误的时候,我们可以在两个地方捕获:
- Global里面的Application_Error 。
-
HandleErrorAttribute 中的OnException。(需要新建一个类,继承HandleErrorAttribute)
那我们到底应该在哪里处理错误好呢?下面我来给大家说说他们的区别。
Application_Error
程序中发生的所有异常,都可以在这里捕获。但是有一个不足的地方,那就是,如果我们在这里捕获异常之后,需要显示错误页面,那么浏览器上的地址是会改变的,已经被重定向了。也就是说,浏览器上显示的地址,并不是真正发生错误的地址,而是仅仅是显示错误信息的页面地址。这个不足,跟在Web.config的customError中配置错误页面是一样的。
很多时候,我们是希望在访问某个URL后发生了错误,显示错误页面之后,URL还是不变。因此,在这里处理异常并不能满足这种需求。
OnException。(继承HandleErrorAttribute)
MVC的特点就是,每一个请求都对应一个Controller,当在某个Controller中发生异常时,我们都可以在OnException中捕获。但是,如果根本就找不到这个Controller,那么这种错误OnException就无能为力了。举个例子:
假设有个Controller叫About,当访问http://host/About/Index发生错误时,是可以在OnException中捕获的。但如果我访问http://host/Aboute/Index的时候,因为根本不存在Aboute控制器,所以这种错误是无法在OnException捕获的,如果要捕获这种错误,只能在Application_Error中处理。虽然OnException不能捕获所有的错误,但是,它可以解决Application_Error错误页面重定向的问题,在显示错误页面的时候,URL保持不变。
执行顺序
如果发生了错误,会先执行OnException,如果设置 filterContext.ExceptionHandled = true; 则说明该错误已被处理,不会再执行Application_Error,否则会继续执行Application_Error。
参考代码
protected void Application_Error(Object sender, EventArgs e) { //当路径出错,无法找到控制器时,不会执行FilterConfig中的OnException,而会在这里捕获。 //当发生404错误时,执行完OnException后,还会执行到这里。 //当发生其他错误,会执行OnException,但在base.OnException中已经处理完错误,不会再到这里执行。 var lastError = Server.GetLastError(); if (lastError != null) { var httpError = lastError as HttpException; if (httpError != null) { //Server.ClearError(); switch (httpError.GetHttpCode()) { case 404: Response.Redirect("/Views/Static/404.html"); break; } } } }
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] public class LogExceptionAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { string controllerName = (string)filterContext.RouteData.Values["controller"]; string actionName = (string)filterContext.RouteData.Values["action"]; HandleErrorInfo info = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); HttpRequestBase request = filterContext.RequestContext.HttpContext.Request; string broser = request.Browser.Browser; string broserVersion = request.Browser.Version; string system = request.Browser.Platform; string errBaseInfo = string.Format("UserId={0},Broser={1},BroserVersion={2},System={3},Controller={4},Action={5}", AuthAttribute.GetUserId(), broser, broserVersion, system, controllerName, actionName); LogUtil.Error(errBaseInfo, filterContext.Exception, Website.LOG_ID); if (!filterContext.ExceptionHandled) { if (filterContext.HttpContext.IsCustomErrorEnabled) { filterContext.HttpContext.Response.Clear(); HttpException httpex = filterContext.Exception as HttpException; if (httpex != null) { filterContext.HttpContext.Response.StatusCode = httpex.GetHttpCode(); // info = new HandleErrorInfo(ex, controllerName, actionName); //switch (httpex.GetHttpCode()) //{ // case 403: // break; // case 404: // break; // default: // base.OnException(filterContext); // break; //} } else { filterContext.HttpContext.Response.StatusCode = 500; } filterContext.Result = new ViewResult() { ViewName = "/Views/Shared/Error.cshtml", ViewData = new ViewDataDictionary<HandleErrorInfo>(info) }; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; } else { //base.OnException(filterContext); // return; //当customErrors=Off时 //当customErrors = RemoteOnly,且在本地调试时 filterContext.Result = new ViewResult() { ViewName = "/Views/Shared/ErrorDetails.cshtml", ViewData = new ViewDataDictionary<HandleErrorInfo>(info) }; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; } } } }