zoukankan      html  css  js  c++  java
  • 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()

    效果如下:

  • 相关阅读:
    javascript Date类的扩展
    软件工程师好了歌 (转)
    您可能不知道的.Net2.0小技巧
    您未必知道的Js技巧
    复活吧,架构师!
    技巧系列文章
    不要使用paddingtop控制内容开始的位置
    JQuery Offset实验与应用(转载)
    2008最佳Windows应用程序
    精选15个国外CSS框架
  • 原文地址:https://www.cnblogs.com/yanglang/p/6961417.html
Copyright © 2011-2022 走看看