在MVC框架中,异常处理是一个重要的议题。它不仅可以帮助更好地管理程序中的错误,还可以提高用户体验。本文将详细讨论MVC中的异常处理机制,包括本地级别的异常处理和全局级别的异常处理。
在开始异常处理的演示之前,需要创建一个示例项目。这将帮助更好地理解异常处理的工作原理和实现方式。
本地级别的异常处理通常涉及到在控制器的每个动作方法中使用try-catch块。以下是一个简单的例子:
public ActionResult TestMethod()
{
try
{
// 业务逻辑代码
return View();
}
catch (Exception e)
{
// 异常处理逻辑
return View("Error");
}
}
这种方式的问题在于,不能在多个动作方法之间重用异常处理逻辑。为了解决这个问题,可以使用控制器的OnException方法。
覆盖控制器的OnException方法可以让在整个控制器的所有动作方法中共享异常处理逻辑。以下是如何实现的示例:
protected override void OnException(ExceptionContext filterContext)
{
Exception e = filterContext.Exception;
// 记录异常
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = "Error";
};
}
这样,就可以在控制器级别共享异常处理逻辑了。但是,如果想要在多个控制器之间共享异常处理逻辑,就需要使用全局异常处理。
全局异常处理可以通过在Web.config文件中启用自定义错误来实现。以下是如何配置的示例:
<customerrors mode="On" />
配置完成后,当应用程序发生错误时,会自动显示错误视图。但是,还没有指定错误页面(视图)的名称,这是为什么呢?
FilterConfig类负责注册全局过滤器。以下是如何实现的示例:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
HandleErrorAttribute被添加到全局过滤器集合中。这个方法在Global.asax文件的Application_Start事件中被调用。它的作用是处理所有控制器中所有动作方法抛出的异常,并返回共享文件夹中的错误视图。
如果希望在控制器级别使用HandleErrorAttribute,可以按照以下方式操作:
[HandleError]
public class TestingController : Controller
{
// 控制器中的动作方法
}
这样,TestingController中所有动作方法抛出的异常都会被处理。其他未处理的异常将被视为未处理异常。
如果希望在动作方法级别使用HandleErrorAttribute,可以按照以下方式操作:
public class TestingController : Controller
{
[HandleError]
public ActionResult TestMethod()
{
// 动作方法的业务逻辑
}
}
这样,TestMethod方法抛出的异常将被处理。
为了在错误视图中显示错误详情,可以将错误视图设置为System.Web.Mvc.HandleErrorInfo模型的强类型视图。然后,可以使用@Model关键字访问成员。其中一个成员是Exception对象。
可以使用HandleErrorAttribute构造函数的重载版本来为不同类型的异常显示不同的视图。以下是如何实现的示例:
[HandleError(ExceptionType = typeof(ArgumentException), View = "ArgumentExceptionView")]
public ActionResult TestMethod()
{
// 动作方法的业务逻辑
}
这样,当TestMethod方法抛出ArgumentException类型的异常时,将显示ArgumentExceptionView视图。
HandleErrorAttribute有一些局限性,例如: 1. 错误不会被记录到任何地方。 2. 控制器外部抛出的异常不会被处理。例如,由于无效的URL导致的异常不会被处理。 3. 不能根据场景进行异常处理。例如,不能为Ajax请求和普通请求提供不同的错误页面。
可以通过扩展默认的HandleErrorAttribute来克服上述一些局限性。以下是如何实现的示例:
private bool IsAjax(ExceptionContext filterContext)
{
return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
if (IsAjax(filterContext))
{
filterContext.Result = new JsonResult() { Data = filterContext.Exception.Message, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
}
else
{
base.OnException(filterContext);
}
// 在这里编写错误记录代码
}
通过扩展HandleErrorAttribute,现在可以记录错误,并且对异常处理有更多的控制。现在可以检查请求是Ajax请求还是普通请求,并相应地进行处理。
“资源未找到”异常既不能被HandleErrorAttribute处理,也不能被CustomHandleErrorAttribute处理。但是,可以使用传统的Web.config方法来处理这种异常。以下是如何配置的示例:
<customerrors mode="On">
<error statuscode="404" redirect="~/Testing/NoPageFound" />
</customerrors>
Application_Error是一个应用程序级别的事件,在global.asax文件中定义。每当应用程序中出现未处理的异常时,它都会被执行。既然如此,为什么不总是使用Application_Error呢?