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请求返回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

posted @ 2014-05-27 00:51  Darren Ji  阅读(2730)  评论(2编辑  收藏  举报

我的公众号:新语新世界,欢迎关注。