[原]ASP.NET MVC異常處理方案

來源:互聯網
上載者:User

異常處理是每一個系統都必須要有的功能,尤其對於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()

效果如下:

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.