NET-MVC全局异常处理二

40次阅读

共计 3320 个字符,预计需要花费 9 分钟才能阅读完成。

对上节的内容进行了补充

MVC 过滤器 Filter

MVC 有四种过滤器:Authorization、Exception、Action、Result,我们要用的的就是 Exception 异常过滤器

当我们新建一个 MVC 项目后,异常过滤器就已经自动在程序中注册了,先从上一节所说的全局配置文件开始,Global.asax 这个文件中的 Application_Start 方法会在程序启动时运行,其中的即默认注册了全局过滤器,如图

我们可以进入 RegisterGlobalFilters 方法查看,这个方法中默认注册了一个异常处理过滤器,也就是说默认状态的 MVC 程序发生异常时会被程序捕获处理,处理方式是跳转至错误页面,也就是上一篇文章说的 Layout 文件夹下面的 Error 页

但使用异常过滤器有一个大前提是要在 Web.config 中打开自定义错误处理的设置,customErrors 节点要设置为“On”, 这一设置默认是关闭的,也就是说要手动加上才行

<system.web>
  <compilation debug="true" targetFramework="4.6.1"/>
  <httpRuntime targetFramework="4.6.1"/>
  <customErrors mode="On">
  </customErrors>
</system.web>

需要注意的是 404 错误,这种类型的异常并不会被过滤器捕获

但是可以在 web.config 中添加节点进行自定义配置,跳转到相应的页面

<customErrors mode="On">
  <error redirect="~/Error/NotFound" statusCode="404" />
</customErrors>
public class ErrorController : Controller
{
    // GET: Error
    public ActionResult Index()
    {return View();
    }

    public ActionResult CustomHttpError()
    {
        ViewBag.Message = "有错误";
        //HandleErrorInfo
        //ExceptionContext

        return View();}
    public ActionResult NotFound()
    {return View();
    }
}

自定义过滤器

MVC 默认的异常过滤器可以满足基本的需要,但是如果要对一些异常进行特殊处理就需要我们自定义过滤器的内容,可以通过重写 OnException 方法达到这个目的

public class CustomHandleErrorAttribute : HandleErrorAttribute
{public override void OnException(ExceptionContext filterContext)
    {
        /* 调用基类的 OnException 方法,实现基础的功能。* 如果要完全的自定义,就不需要调用基类的方法
         */
        base.OnException(filterContext);

        /* 此处可进行记录错误日志,发送错误通知等操作
         * 通过 Exception 对象和 HttpException 对象可获取相关异常信息。* Exception exception = filterContext.Exception;
         * HttpException httpException = new HttpException(null, exception);
         */
    }
}

示例代码

public class MyErrorHandler : FilterAttribute, IExceptionFilter
{public void OnException(ExceptionContext filterContext)
    {if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
            return;

        var statusCode = (int) HttpStatusCode.InternalServerError;
        if (filterContext.Exception is HttpException)
        {statusCode = filterContext.Exception.As<HttpException>().GetHttpCode();}
        else if (filterContext.Exception is UnauthorizedAccessException)
        {
            //to prevent login prompt in IIS
            // which will appear when returning 401.
            statusCode = (int)HttpStatusCode.Forbidden;
        }
        _logger.Error("Uncaught exception", filterContext.Exception);

        var result = CreateActionResult(filterContext, statusCode);
        filterContext.Result = result;

        // Prepare the response code.
        filterContext.ExceptionHandled = true;
        filterContext.HttpContext.Response.Clear();
        filterContext.HttpContext.Response.StatusCode = statusCode;
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
    }

    protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
    {var ctx = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
        var statusCodeName = ((HttpStatusCode) statusCode).ToString();

        var viewName = SelectFirstView(ctx,
                                       "~/Views/Error/{0}.cshtml".FormatWith(statusCodeName),
                                       "~/Views/Error/General.cshtml",
                                       statusCodeName,
                                       "Error");

        var controllerName = (string) filterContext.RouteData.Values["controller"];
        var actionName = (string) filterContext.RouteData.Values["action"];
        var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
        var result = new ViewResult
                         {
                             ViewName = viewName,
                             ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
                         };
        result.ViewBag.StatusCode = statusCode;
        return result;
    }

    protected string SelectFirstView(ControllerContext ctx, params string[] viewNames)
    {return viewNames.First(view => ViewExists(ctx, view));
    }

    protected bool ViewExists(ControllerContext ctx, string name)
    {var result = ViewEngines.Engines.FindView(ctx, name, null);
        return result.View != null;
    }
}

正文完
 0