ASP.NET MVC中的error handling

我怎样才能正确处理从ASP.NET MVC中的控制器引发的exception? HandleError属性似乎只处理由MVC基础设施抛出的exception,而不是由我自己的代码引发的exception。

使用这个web.config

 <customErrors mode="On"> <error statusCode="401" redirect="/Errors/Http401" /> </customErrors> 

用下面的代码

 namespace MvcApplication1.Controllers { [HandleError] public class HomeController : Controller { public ActionResult Index() { // Force a 401 exception for testing throw new HttpException(401, "Unauthorized"); } } } 

不会导致我所希望的。 相反,我得到了通用的ASP.NET错误页面,告诉我修改我的web.config来查看实际的错误信息。 但是,如果不是抛出一个exception,我返回一个无效的视图,我得到/Shared/Views/Error.aspx页面:

 return View("DoesNotExist"); 

像上面所做的那样在控制器中抛出exception似乎绕过了HandleError所有function,那么创build错误页面的正确方法是什么,以及如何与MVC基础架构打好关系?

Controller.OnException(ExceptionContext context) 。 覆盖它。

 protected override void OnException(ExceptionContext filterContext) { // Bail if we can't do anything; app will crash. if (filterContext == null) return; // since we're handling this, log to elmah var ex = filterContext.Exception ?? new Exception("No further information exists."); LogException(ex); filterContext.ExceptionHandled = true; var data = new ErrorPresentation { ErrorMessage = HttpUtility.HtmlEncode(ex.Message), TheException = ex, ShowMessage = !(filterContext.Exception == null), ShowLink = false }; filterContext.Result = View("ErrorPage", data); } 

感谢kazimanzurrashaid,这里是我在Global.asax.cs中完成的工作:

 protected void Application_Error() { Exception unhandledException = Server.GetLastError(); HttpException httpException = unhandledException as HttpException; if (httpException == null) { Exception innerException = unhandledException.InnerException; httpException = innerException as HttpException; } if (httpException != null) { int httpCode = httpException.GetHttpCode(); switch (httpCode) { case (int) HttpStatusCode.Unauthorized: Response.Redirect("/Http/Error401"); break; } } } 

根据我需要支持的任何其他HTTP错误代码,我将能够添加更多页面到HttpContoller。

HandleError属性似乎只处理由MVC基础设施抛出的exception,而不是由我自己的代码引发的exception。

这是错误的。 事实上,HandleError只会“处理”抛出在你自己的代码中的exception,或者是由你自己的代码调用的代码。 换句话说,只有您的操作在调用堆栈中的exception。

你所看到的行为的真正解释是你抛出的具体exception。 HandleError与HttpException的行为不同。 从源代码:

  // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method), // ignore it. if (new HttpException(null, exception).GetHttpCode() != 500) { return; } 

我不认为你将能够显示具有HandleError属性的基于HttpCode的特定的ErrorPage,我宁愿使用HttpModule来实现这个目的。 假设我有文件夹“ErrorPages”,其中每个特定错误都存在不同的页面,并且映射在web.config中与常规Web表单应用程序相同。 以下是用于显示错误页面的代码:

 public class ErrorHandler : BaseHttpModule{ public override void OnError(HttpContextBase context) { Exception e = context.Server.GetLastError().GetBaseException(); HttpException httpException = e as HttpException; int statusCode = (int) HttpStatusCode.InternalServerError; // Skip Page Not Found and Service not unavailable from logging if (httpException != null) { statusCode = httpException.GetHttpCode(); if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable)) { Log.Exception(e); } } string redirectUrl = null; if (context.IsCustomErrorEnabled) { CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors"); if (section != null) { redirectUrl = section.DefaultRedirect; if (httpException != null) { if (section.Errors.Count > 0) { CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)]; if (item != null) { redirectUrl = item.Redirect; } } } } } context.Response.Clear(); context.Response.StatusCode = statusCode; context.Response.TrySkipIisCustomErrors = true; context.ClearError(); if (!string.IsNullOrEmpty(redirectUrl)) { context.Server.Transfer(redirectUrl); } } 

}

另外一种可能性(你的情况并非如此),其他读者可能正在经历的是你的错误页面本身正在抛出一个错误,或者没有实现:

  System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo> 

如果是这种情况,那么你会得到默认的错误页面(否则你会得到一个无限循环,因为它会不断尝试将自己发送到您的自定义错误页面)。 这对我来说并不是显而易见的。

这个模型是发送到错误页面的模型。 如果您的错误页面使用与您网站其他部分相同的主页面,并且需要任何其他模型信息,那么您将需要创build您自己的[HandleError]types的属性或重写OnException或其他内容。

我select了Controller.OnException()方法,对我来说这是合乎逻辑的select – 因为我select了ASP.NET MVC,所以我宁愿留在框架级别,并尽可能避免与底层机制混淆。

我遇到了以下问题:

如果在视图内发生exception,则来自该视图的部分输出将与错误消息一起出现在屏幕上。

我通过在设置filterContext.Result之前清除响应来解决这个问题 – 像这样:

  filterContext.HttpContext.Response.Clear(); // gets rid of any garbage filterContext.Result = View("ErrorPage", data); 

希望这可以节省别人一些时间:-)

  protected override void OnException (ExceptionContext filterContext ) { if (filterContext != null && filterContext.Exception != null) { filterContext.ExceptionHandled = true; this.View("Error").ViewData["Exception"] = filterContext.Exception.Message; this.View("Error").ExecuteResult(this.ControllerContext); } } 

Jeff Atwood的用户友好的exception处理模块非常适合MVC。 你可以在你的web.config中完全configuration它,根本没有MVC项目源代码的变化。 但是,它需要一个小的修改来返回原始的HTTP状态,而不是200状态。 看到这个相关的论坛post 。

基本上,在Handler.vb中,可以添加如下内容:

 ' In the header... Private _exHttpEx As HttpException = Nothing ' At the top of Public Sub HandleException(ByVal ex As Exception)... HttpContext.Current.Response.StatusCode = 500 If TypeOf ex Is HttpException Then _exHttpEx = CType(ex, HttpException) HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode() End If