当前位置: 首页 > 工具软件 > ELMAH > 使用案例 >

MVC扩展Filter,通过继承HandleErrorAttribute,使用log4net或ELMAH组件记录服务端500错误、HttpException、Ajax异常等...

姬俊远
2023-12-01

□ 接口

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请求返回json
if (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);
}
}
}
}
 

 

参考资料:
Exception Handling in ASP.NET MVC

 类似资料: