MVC自定义错误页面

  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>
Web.Config
@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>
Error.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;
            }
        }
    }
LogExceptionAttribute

错误页写入异常信息与方法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();
                }
            }
        }
    }
Log

4.还可以通过Global.apsx添加Application_Error方法来实现,但实验过程中不怎么喜欢这种方案,故省略。

原文地址:https://www.cnblogs.com/fuxuyang/p/7364430.html