MVC异常处理主要有三种方案:1.基于HandleErrorAttribute重写OnException方法;2.基于Global.apsx添加Application_Error方法;3.直接在Web.Config中配置。现基于上述思路,测试了下面三种自定义错误页面的处理方法(主要侧重于显示异常信息,便于快速找到代码中的异常来源),以便后续查阅。不足之处,还请指教!
1.直接在web.config的<system.web>节点下加入<customErrors mode="On" />,在View/Shared/Error.cshtml中写入异常信息,发生异常时会跳转到该l页面。
<system.web> <customErrors mode="On" /> <compilation debug="true" targetFramework="4.0"> <assemblies> <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> </assemblies> </compilation> <authentication mode="Forms"> <forms loginUrl="~/Account/LogOn" timeout="2880" /> </authentication> <pages> <namespaces> <add namespace="System.Web.Helpers" /> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> <add namespace="System.Web.WebPages"/> </namespaces> </pages> </system.web>
@model HandleErrorInfo @{ Layout = null; } <!DOCTYPE html> <html> <head> <title>Error</title> </head> <body> <p>Message: @Model.Exception.Message</p> <p>Controller: @Model.ControllerName</p> <p>Action: @Model.ActionName</p> <p>Source: @Model.Exception.Source</p> <p>Exception: @Model.Exception</p> </body> </html>
2.新建类继承HandleErrorAttribute并重写OnException方法,将异常信息写入日志文件,并跳转到View/Shared/Error.cshtml错误页。直接将该属性作用于某控制器或者注册为全局变量以便于全局适用。
[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); Log log = new Log(); log.Write(filterContext); if (!filterContext.ExceptionHandled) { filterContext.Result = new ViewResult() { ViewName = "/Views/Shared/Error.cshtml", ViewData = new ViewDataDictionary<HandleErrorInfo>(info) }; filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; } } }
错误页写入异常信息与方法1相同。
[LogException] public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { var x=1; string y="x"; ViewBag.t = x / Convert.ToInt32(y); return View(); } }
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
//new
filters.Add(new LogExceptionAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // 路由名称
"{controller}/{action}/{id}", // 带有参数的 URL
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
3.新建继承于IController的基础控制器,重写OnException方法及写入异常日志文件,其他控制器只需要继承该基础控制器即可。与方法3的区别在于不需要注册全局筛选器取而代之的是控制器的继承。
public class BaseController : Controller { protected 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); DateTime dt = DateTime.Now; string logPath = Server.MapPath("~/Logs/" + dt.ToString("yyyy-MM")); if (!Directory.Exists(logPath)) { Directory.CreateDirectory(logPath); } string logFilePath = string.Format("{0}/{1}.txt", logPath, dt.ToString("yyyy-MM-dd")); StreamWriter writer = null; try { writer = new StreamWriter(logFilePath, true, Encoding.UTF8); writer.WriteLine("------------------------------------------------------------------------------"); writer.WriteLine("出错时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); writer.WriteLine("错误信息:" + filterContext.Exception.Message); writer.WriteLine("Controller:" + filterContext.Controller); writer.WriteLine("错误源:" + filterContext.Exception.Source); writer.WriteLine("堆栈信息:" + filterContext.Exception.StackTrace); writer.WriteLine("------------------------------------------------------------------------------"); } catch { } finally { if (writer != null) { writer.Close(); } } base.OnException(filterContext); filterContext.Result = new ViewResult() { ViewName = "/Views/Shared/Error.cshtml", ViewData = new ViewDataDictionary<HandleErrorInfo>(info) }; } public ActionResult Error() { return View(); } }
public class HomeController : BaseController { // // GET: /Home/ public ActionResult Index() { var x=1; string y="x"; ViewBag.t = x / Convert.ToInt32(y); return View(); } }
备注:异常日志记录类
public class Log { /// <summary> /// 异常信息记录 /// </summary> /// <param name="filterContext"></param> public void Write(ExceptionContext filterContext) { DateTime dt = DateTime.Now; string logPath = HttpContext.Current.Server.MapPath("~/Logs/" + dt.ToString("yyyy-MM")); if (!Directory.Exists(logPath)) { Directory.CreateDirectory(logPath); } string logFilePath = string.Format("{0}/{1}.txt", logPath, dt.ToString("yyyy-MM-dd")); StreamWriter writer = null; try { writer = new StreamWriter(logFilePath, true, Encoding.UTF8); writer.WriteLine("------------------------------------------------------------------------------"); writer.WriteLine("出错时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); writer.WriteLine("错误信息:" + filterContext.Exception.Message); writer.WriteLine("Controller:" + filterContext.Controller); writer.WriteLine("错误源:" + filterContext.Exception.Source); writer.WriteLine("堆栈信息:" + filterContext.Exception.StackTrace); writer.WriteLine("------------------------------------------------------------------------------"); } catch { } finally { if (writer != null) { writer.Close(); } } } }
4.还可以通过Global.apsx添加Application_Error方法来实现,但实验过程中不怎么喜欢这种方案,故省略。