MVC扩展Filter,通过继承HandleErrorAttribute,使用log4net或ELMAH组件记录服务端500错误、HttpException、Ajax异常等
□ 接口
public interface IExceptionFilter
{
void
OnException(ExceptionContext filterContext);
}
ExceptionContext继承于ControllerContext,从中可以获得路由数据route data、HttpContext。
□ 的HandleErrorAttribute是对IExceptionFilter的实现,默认是启用的
public static void RegisterGlobalFilters(GlobalFiltersCollection
filters)
{
filters.Add(new
HandleErrorAttribute());
}
使用默认的HandleErrorAttribute
□ 让Shared/Error.cshtml的出错页报错
前提是,在Web.config中配置:
<customErrors mode="On"></customErrors>
□ 根据不同错误类型显示不同的错误页
[HandleError(ExceptionType =
typeof(DbException),View = "")]
[HandleError(ExceptionType = typeof(ApplicationException), View =
"")]
public ActionResult
SomeAction()
□ HandleErrorAttribute的不足之处
1、只是显示错误页,无法记录错误日志
2、只能捕获500错误
3、不能捕获Controller以外的错误
继承HandleErrorAttribute自定义异常处理
使用log4net记录错误日志,并能记录AJAX错误,返回状态码为500的服务端错误。
需要一个显示错误信息的类:
namespace MvcApplication1.Models
{
public class
HandleErrorInfo
{
public HandleErrorInfo(Exception
exception, string actionName, string
controllerName)
{
this.Exception =
exception;
this.ControllerName =
controllerName;
this.ActionName = actionName;
}
public string ActionName { get;
set; }
public string
ControllerName { get; set; }
public Exception Exception { get; set; }
}
}
引用log4net组件,继承HandleErrorAttribute自定义异常,使之能记录状态码为500的服务端错误,并能以json形式返回ajax相关异常。
using System.Web;using System.Web.Mvc;using log4net;namespace MvcApplication1.Extension{public class PowerfulHandleErrorAttribute : HandleErrorAttribute
{private readonly ILog _logger;
public PowerfulHandleErrorAttribute() { _logger = LogManager.GetLogger("MyLogger");}
public override void OnException(ExceptionContext filterContext)
{ if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) { return;}
if (new HttpException(null, filterContext.Exception).GetHttpCode() != 500)
{ return;}
if (!ExceptionType.IsInstanceOfType(filterContext.Exception)) { return;}
//如果是AJAX请求返回jsonif (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{ filterContext.Result = new JsonResult() {JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new { error = true,message = filterContext.Exception.Message
}
};
}
else {var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); filterContext.Result = new ViewResult() {ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary(model),TempData = filterContext.Controller.TempData
};
}
_logger.Error(filterContext.Exception.Message, filterContext.Exception);
filterContext.ExceptionHandled = true;filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;}
}
}
以上PowerfulHandleErrorAttribute错误,只能处理服务端状态码500错误。还可以在全局中设置:当出现HttpException异常的时候,返回对应的错误提醒视图。
//处理filter遗漏的错误protected void Application_Error(object sender, EventArgs e)
{var httpContext = ((MvcApplication) sender).Context;
var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)); var currentController = ""; var currentAction = "";if (currentRouteData != null)
{if (currentRouteData.Values["controller"] != null &&
!string.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
{ currentController = currentRouteData.Values["controller"].ToString();}
if (currentRouteData.Values["action"] != null &&
!string.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
{ currentAction = currentRouteData.Values["action"].ToString();}
}
var ex = Server.GetLastError();
var controller = new ErrorController(); var routeData = new RouteData(); var action = "Index";if (ex is HttpException)
{ var httpEx = ex as HttpException; switch (httpEx.GetHttpCode()) { case 404: action = "NotFound"; break; default: action = "Index"; break;}
}
httpContext.ClearError();
httpContext.Response.Clear();
httpContext.Response.StatusCode = ex is HttpException ? ((HttpException) ex).GetHttpCode() : 500; httpContext.Response.TrySkipIisCustomErrors = true;routeData.Values["controller"] = "Error";
routeData.Values["action"] = action; controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}
为此,还需要定义一个ErrorController,当然还有与之对应的错误提示视图:
using System.Web.Mvc;namespace MvcApplication1.Controllers{public class ErrorController : Controller
{ public ActionResult Index() { return View();}
public ActionResult NotFound() { return View();}
}
}
使用ELMAH记录全局异常
using System.Web.Mvc;using Elmah;namespace MvcApplication1.Extension{public class ElmahHandleErrorAttribute : HandleErrorAttribute
{public override void OnException(ExceptionContext filterContext)
{ base.OnException(filterContext); if (filterContext.ExceptionHandled) {ErrorSignal.FromCurrentContext().Raise(filterContext.Exception);
}
}
}
}