ASP.NET MVC异常处理方案

异常处理是每一个系统都必须要有的功能,尤其对于Web系统而言,简单、统一的异常处理模式尤为重要,当打算使用ASP.NET MVC来做项目时,第一个数据录入页面就遇到了这个问题。

在之前的ASP.NET WebForm项目中,一般情况下都是在Application_Error事件处理器和ScriptManager_AsyncPostBackError事件处理器里面进行,在ASP.NET MVC中用这两种方法似乎都不合适了,该放在哪儿呢?总不至于在每个Action里面都放一个try{...}catch{...}吧。

在ScottGu的博客里面提到了一个类:HandleErrorAttribute,似乎是用于处理异常的,于是使用HandleErrorAttribute来做个尝试,(说明,如果使用了该类型,并且想把异常显示在自已指定的View,则必须在web.config里面的<system.web>节点加上<customErrors mode="On" />)发现HandleError的确比较好用,可以使用其View属性指定异常后跳转的页面,可以针对不同的异常类型跳到不同的异常显示View,而且也可以不跳转到异常显示View,显示到当前View,例:

[HttpPost]
[HandleError(View = "Create" , ExceptionType = typeof (Exception))]
public  ActionResult Create( string  someParam)
{
     throw  new  Exception( "oops..." );
}

当异常发生时,页面还会跳回到Create,只是这里有点小问题,用户在页面上输入了很多东西,你提示个异常不至于把他辛辛苦苦输了半天的东西都没有了吧,把这样的项目送出去,迟早是要改回来的。

打开HandleErrorAttribute的源代码可以看其关键部分:

public  virtual  void  OnException(ExceptionContext filterContext) {
     if  (filterContext == null ) {
         throw  new  ArgumentNullException( "filterContext" );
     }
     if  (filterContext.IsChildAction) {
         return ;
     }
 
     // If custom errors are disabled, we need to let the normal ASP.NET exception handler
     // execute so that the user can see useful debugging information.
     if  (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) {
         return ;
     }
 
     Exception exception = filterContext.Exception;
 
     // 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 ;
     }
 
     if  (!ExceptionType.IsInstanceOfType(exception)) {
         return ;
     }
 
     string  controllerName = ( string )filterContext.RouteData.Values[ "controller" ];
     string  actionName = ( string )filterContext.RouteData.Values[ "action" ];
     HandleErrorInfo model = new  HandleErrorInfo(filterContext.Exception, controllerName, actionName);
     filterContext.Result = new  ViewResult {
         ViewName = View,
         MasterName = Master,
         ViewData = new  ViewDataDictionary<HandleErrorInfo>(model),
         TempData = filterContext.Controller.TempData
     };
     filterContext.ExceptionHandled = true ;
     filterContext.HttpContext.Response.Clear();
     filterContext.HttpContext.Response.StatusCode = 500;
 
     // Certain versions of IIS will sometimes use their own error page when
     // they detect a server error. Setting this property indicates that we
     // want it to try to render ASP.NET MVC's error page instead.
     filterContext.HttpContext.Response.TrySkipIisCustomErrors = true ;
}

可以很清楚的看到,MVC实际上是使用刚才我们指定的View名称新建了一个ViewResult,然后将这个ViewResult交给了InvokeActionResult方法,最终显示给了用户。在这个过程中,新的ViewResult的ViewData被设定为HandleErrorInfo了,没有将Create上的数据放进ViewData,尽管在之后显示的Create视图的Request里还保存着之前的Params内容,但是数据却没有加载上去,我也没有去深究,感觉如果在这里直接把filterContext.Controller中的ViewData直接作为新的ViewResult的ViewData的话,肯定是可以显示提交之前的数据的(因为如果将异常代码包在try...catch...里面是可以在异常后显示之前数据的)。

于是自已新建一个ExceptionFitler:

public  class  CustomHandleErrorAttribute : FilterAttribute, IExceptionFilter
{
     public  void  OnException(ExceptionContext filterContext)
     {
         filterContext.Controller.ViewData[ "Exception" ] = filterContext.Exception;
         filterContext.Result = new  ViewResult() { ViewName = filterContext.Controller.ControllerContext.RouteData.Values[ "Action" ].ToString(), ViewData = filterContext.Controller.ViewData };
         filterContext.ExceptionHandled = true ;
         filterContext.HttpContext.Response.TrySkipIisCustomErrors = true ;
     }
}

类名起的不咋的,将就着用吧:)

将原来的Action修改如下:

[HttpPost]
[CustomHandleError]
public  ActionResult Create( string  Name)
{
     throw  new  Exception( "oops..." );
}

Create.csthml中加入如下代码:

if  (ViewData[ "Exception" ] != null )
{
     var  ex = ViewData[ "Exception" ] as  Exception;
     @ex.Message
}

F5,果然在提交后又回到了原来视图,而且之前填写的数据都还在。

3月19日完善如下:-----------------------------------------

namespace  System.Web.Mvc
{
     public  class  HandleExceptionAttribute : HandleErrorAttribute, IExceptionFilter
     {
         #region IExceptionFilter Members
 
         public  override  void  OnException(ExceptionContext filterContext)
         {
             if  (filterContext == null )
             {
                 throw  new  ArgumentNullException( "filterContext" );
             }
             if  (filterContext.IsChildAction)
             {
                 return ;
             }
 
             // If custom errors are disabled, we need to let the normal ASP.NET exception handler
             // execute so that the user can see useful debugging information.
             if  (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
             {
                 return ;
             }
 
             Exception exception = filterContext.Exception;
 
             // 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 ;
             }
 
             if  (!ExceptionType.IsInstanceOfType(exception))
             {
                 return ;
             }
 
             string  actionName = ( string )filterContext.RouteData.Values[ "action" ];
             filterContext.Controller.ViewData[ "Exception" ] = exception;
             filterContext.Result = new  ViewResult() { ViewName = actionName, ViewData = filterContext.Controller.ViewData };
             filterContext.ExceptionHandled = true ;
             filterContext.HttpContext.Response.Clear();
             filterContext.HttpContext.Response.StatusCode = 500;
             filterContext.HttpContext.Response.TrySkipIisCustomErrors = true ;
         }
 
         #endregion
     }
 
     public  static  class  HandleExceptionHelper
     {
         public  static  Exception Exception( this  HtmlHelper htmlhelper)
         {
             var  exception = htmlhelper.ViewContext.Controller.ViewData[ "Exception" ] as  Exception;
             return  exception;
         }
     }
}

View运用如下:

if  (@Html.Exception() != null )
{
     @Html.Exception().Message
}

3月20日添加生成jQuery错误样式:------------------------------------------------

public  static  class  HandleExceptionHelper
{
     public  static  Exception Exception( this  HtmlHelper htmlhelper)
     {
         var  exception = htmlhelper.ViewContext.Controller.ViewData[ "Exception" ] as  Exception;
         return  exception;
     }
 
     public  static  MvcHtmlString jQueryStyleError( this  HtmlHelper htmlhelper)
     {
         var  exception = Exception(htmlhelper);
 
         if  (exception == null )
         {
             return  null ;
         }
 
         TagBuilder builder = new  TagBuilder( "div" );
         builder.GenerateId( "editordescription" );
         builder.AddCssClass( "ui-widget ui-state-error ui-corner-all" );
         builder.InnerHtml = string .Format( @"<p><span class=""ui-icon ui-icon-alert"" style=""float: left; margin-right: .3em;""></span><strong>{0}: </strong>{1}</p>" ,
             Resx.Error, string .IsNullOrEmpty(exception.Message) ? Resx.UnknowErrorMessage : exception.Message);
 
         return  new  MvcHtmlString(builder.ToString(TagRenderMode.Normal));
     }
}

View应用如下:

@Html.jQueryStyleError()

效果如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值