对于Asp.Net MVC 项目中,对于异常情况下,会跳转到自己定义好的页面,这时就用到了MVC中的异常过滤器(Exception Filters)
(1)一旦action 方法中出现异常,异常过滤器就会控制程序的运行过程,开始内部自动写入运行的代码。MVC为我们提供了编写好的异常过滤器:HandeError。
(2)当action方法中发生异常时,过滤器就会在“~/Views/[current controller]”或“~/Views/Shared”目录下查找到名称为”Error”的View,然后创建该View的ViewResult,并作为响应返回。
一些配置信息
1、在WebConfig中把过滤器配置启动
当自定义异常被捕获时,异常过滤器变为可用。为了能够获得自定义异常,打开Web.config文件,在System.Web.Section下方添加自定义错误信息。
<customErrors mode="On"> </customErrors>
2、创建Error View
在“~/Views/Shared”文件夹下,会发现存在“Error.cshtml”文件,该文件是由MVC 模板提供的,如果没有自动创建,该文件也可以手动完成。
3、绑定异常过滤器
将过滤器绑定到action方法或controller上,不需要手动执行,打开 App_Start folder文件夹中的 FilterConfig.cs文件。在 RegisterGlobalFilters 方法中会看到 HandleError 过滤器已经以全局过滤器绑定成功。
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute());
默认是启动全局过滤器的,也可以删除全局过滤器,那么会将过滤器绑定到action 或controller层,但是不建议这么做,最好是在全局中
[HandleError(ExceptionType = typeof(NullReferenceException), View = "error")] public ActionResult Index() { throw new NullReferenceException(); return View(); }
4、运行过后,在View中显示错误信息
将Error View转换为HandleErrorInfo类的强类型View,并在View中显示错误信息。
@model HandleErrorInfo @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta name="viewport" content="width=device-width" /> <title>错误</title> </head> <body> <hgroup> <h1>错误。</h1> <h2>处理你的请求时出错。1231321</h2> </hgroup> 错误信息:@Model.Exception.Message<br /> 控制器:@Model.ControllerName <br /> 方法: @Model.ActionName </body> </html>
5、运行测试,报错后,显示的页面如下图为:
另外还有种情况,就是Handle error属性能够确保无论是否出现异常,自定义View都能够显示,但是它的能力在controller和action 方法中是受限的。不会处理“Resource not found”这类型的错误。
比如,运行应用程序时,输入一些奇怪的URL
针对这种情况,
(1)我们需要在WebConfig中添加这样一个配置
<customErrors mode="On"> <error statusCode="404" redirect="~/Error/Index"/> </customErrors>
(2)创建ErrorController控制器,并创建Index方法,代码如下:
[AllowAnonymous] public class ErrorController : Controller { public ActionResult Index() { Exception e = new Exception("Invalid Controller or/and Action Name"); HandleErrorInfo eInfo = new HandleErrorInfo(e, "Unknown", "Unknown"); return View("Error", eInfo); } }
[AllowAnonymous]
将AllowAnonymous属性应用到 ErrorController中,因为错误控制器和index方法不应该只绑定到认证用户,也很有可能用户在登录之前已经输入错误的URL。
这样混乱URL的出现的报错信息就跳转到指定的页面了。
接下来的情况,扩展Handler Error,并进行添加日志处理
(1)在根目录下,新建文件夹,命名为Logger。在Logger 文件夹下新建类 FileLogger
public class FileLogger { public void LogException(Exception e) { File.WriteAllLines("C://Error//" + DateTime.Now.ToString("yyyy-MM-dd mm hh ss") + ".txt", new string[] { "Message:"+e.Message, "Stacktrace:"+e.StackTrace }); }
public void LogExceptionContext(ExceptionContext filterContext) { File.WriteAllLines("C://Error//" + DateTime.Now.ToString("yyyy-MM-dd mm hh ss") + ".txt", new string[] { "时间:"+DateTime.Now.ToString("yyyy-MM-dd mm hh ss"), "ip地址:"+filterContext.HttpContext.Request.UserHostAddress, "控制器:"+filterContext.RouteData.Values["Controller"], "动作方法:"+filterContext.RouteData.Values["Action"], "错误信息:"+filterContext.Exception.Message+">>>"+filterContext.Exception.StackTrace }); }
}
(2)创建CommonExceptionFilter类
在 Filters文件夹下,新建 EmployeeExceptionFilter类,继承 HandleErrorAttribute类,重写 OnException方法:
public class CommonExceptionFilter : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { FileLogger logger = new FileLogger(); logger.LogException(filterContext.Exception); base.OnException(filterContext); filterContext.ExceptionHandled = true; } }
(3)修改默认的异常过滤器
在FilterConfig中进行修改
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //filters.Add(new HandleErrorAttribute()); filters.Add(new CommonExceptionFilter());
这样运行,报错的时候,会在C盘下面记录报错的日志。
需要注意的是 filterContext.ExceptionHandled = true;
当返回自定义响应时,做的第一件事情就是通知MVC 引擎,手动处理异常,因此不需要执行默认的操作,不会显示默认的错误页面。
使用filterContext.ExceptionHandled = true;这句话即可实现页面的显示。
另外,我们可以在OnException这个重写方法中加上 ExceptionHandled属性
public override void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled) { FileLogger logger = new FileLogger(); logger.LogException(filterContext.Exception); base.OnException(filterContext); filterContext.ExceptionHandled = true; } }
我们这里还判断了ExceptionHandled属性,这个属性的含义是如果其他的异常过滤器已经处理了该异常,则该属性的值为true,为了能够适应大的范围,所以笔者这里建议读者也要先判断是否已经有其他的异常过滤器已经处理过这个异常了。