ASP.NET MVC中異常Exception攔截的深入理解
一、前言
由于客戶端的環(huán)境不一致,有可能會造成我們預(yù)計不到的異常錯誤,所以在項目中,友好的異常信息提示,是非常重要的。在asp.net mvc中實現(xiàn)異常屬性攔截也非常簡單,只需要繼承另一個類(System.Web.Mvc.FilterAttribute)和一個接口(System.Web.Mvc.IExceptionFilter),實現(xiàn)接口里面OnException方法,或者直接繼承Mvc 提供的類System.Web.Mvc.HandleErrorAttribute。
下面話不多說了,來一起看看詳細(xì)的介紹吧
二、實現(xiàn)關(guān)鍵邏輯
繼承System.Web.Mvc.HandleErrorAttribute,重寫了OnException方法,主要實現(xiàn)邏輯代碼如下:
public class HandlerErrorAttribute : HandleErrorAttribute { /// <summary> /// 控制器方法中出現(xiàn)異常,會調(diào)用該方法捕獲異常 /// </summary> /// <param name="context">提供使用</param> public override void OnException(ExceptionContext context) { WriteLog(context); base.OnException(context); context.ExceptionHandled = true; if (context.Exception is UserFriendlyException) { context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK; context.Result = new ContentResult { Content = new AjaxResult { type = ResultType.error, message = context.Exception.Message }.ToJson() }; } else if (context.Exception is NoAuthorizeException) { context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; if (!context.HttpContext.Request.IsAjaxRequest()) { context.HttpContext.Response.RedirectToRoute("Default", new { controller = "Error", action = "Error401", errorUrl = context.HttpContext.Request.RawUrl }); } else { context.Result = new ContentResult { Content = context.HttpContext.Request.RawUrl }; } } else { context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; ExceptionMessage error = new ExceptionMessage(context.Exception); var s = error.ToJson(); if (!context.HttpContext.Request.IsAjaxRequest()) { context.HttpContext.Response.RedirectToRoute("Default", new { controller = "Error", action = "Error500", data = WebHelper.UrlEncode(s) }); } else { context.Result = new ContentResult { Content = WebHelper.UrlEncode(s) }; } } } /// <summary> /// 寫入日志(log4net) /// </summary> /// <param name="context">提供使用</param> private void WriteLog(ExceptionContext context) { if (context == null) return; if (context.Exception is NoAuthorizeException || context.Exception is UserFriendlyException) { //友好錯誤提示,未授權(quán)錯誤提示,記錄警告日志 LogHelper.Warn(context.Exception.Message); } else { //異常錯誤, LogHelper.Error(context.Exception); ////TODO :寫入錯誤日志到數(shù)據(jù)庫 } } }
MVC 過濾器全局注冊異常攔截:
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandlerErrorAttribute()); } }
我們看到,context.Exception 分為3種:UserFriendlyException,NoAuthorizeException 或 Exception;UserFriendlyException 是指友好異常,前端友好提示錯誤信息。NoAuthorizeException 為401未授權(quán)異常,當(dāng)頁面未被授權(quán)訪問時,返回該異常,并攜帶有未授權(quán)的路徑地址。其他異常統(tǒng)一返回500錯誤,并攜帶異常信息。
三、異常處理
1.401 未授權(quán)錯誤
異常定義代碼:
/// <summary> /// 沒有被授權(quán)的異常 /// </summary> public class NoAuthorizeException : Exception { public NoAuthorizeException(string message) : base(message) { } }
拋出異常代碼:
throw new NoAuthorizeException("未授權(quán)");
前端UI效果:
2.404 未找到頁面錯誤
MVC的404異常處理,有幾種方式,我們采用了在Global.asax全局請求函數(shù)中處理, 請查看以下代碼
protected void Application_EndRequest() { if (Context.Response.StatusCode == 404) { bool isAjax = new HttpRequestWrapper(Context.Request).IsAjaxRequest(); if (isAjax) { Response.Clear(); Response.Write(Context.Request.RawUrl); } else { Response.RedirectToRoute("Default", new { controller = "Error", action = "Error404", errorUrl = Context.Request.RawUrl }); } } }
前端UI效果:
3.500服務(wù)器內(nèi)部錯誤
500異常錯誤拋出的異常信息對象定義:
/// <summary> /// 異常錯誤信息 /// </summary> [Serializable] public class ExceptionMessage { public ExceptionMessage() { } /// <summary> /// 構(gòu)造函數(shù) /// 默認(rèn)顯示異常頁面 /// </summary> /// <param name="ex">異常對象</param> public ExceptionMessage(Exception ex) :this(ex, true) { } /// <summary> /// 構(gòu)造函數(shù) /// </summary> /// <param name="ex">異常對象</param> /// <param name="isShowException">是否顯示異常頁面</param> public ExceptionMessage(Exception ex, bool isShowException) { MsgType = ex.GetType().Name; Message = ex.InnerException != null ? ex.InnerException.Message : ex.Message; StackTrace = ex.StackTrace.Length > 300 ? ex.StackTrace.Substring(0, 300) : ex.StackTrace; Source = ex.Source; Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); Assembly = ex.TargetSite.Module.Assembly.FullName; Method = ex.TargetSite.Name; ShowException = isShowException; var request = HttpContext.Current.Request; IP = Net.Ip; UserAgent = request.UserAgent; Path = request.Path; HttpMethod = request.HttpMethod; } /// <summary> /// 消息類型 /// </summary> public string MsgType { get; set; } /// <summary> /// 消息內(nèi)容 /// </summary> public string Message { get; set; } /// <summary> /// 請求路徑 /// </summary> public string Path { get; set; } /// <summary> /// 程序集名稱 /// </summary> public string Assembly { get; set; } /// <summary> /// 異常參數(shù) /// </summary> public string ActionArguments { get; set; } /// <summary> /// 請求類型 /// </summary> public string HttpMethod { get; set; } /// <summary> /// 異常堆棧 /// </summary> public string StackTrace { get; set; } /// <summary> /// 異常源 /// </summary> public string Source { get; set; } /// <summary> /// 服務(wù)器IP 端口 /// </summary> public string IP { get; set; } /// <summary> /// 客戶端瀏覽器標(biāo)識 /// </summary> public string UserAgent { get; set; } /// <summary> /// 是否顯示異常界面 /// </summary> public bool ShowException { get; set; } /// <summary> /// 異常發(fā)生時間 /// </summary> public string Time { get; set; } /// <summary> /// 異常發(fā)生方法 /// </summary> public string Method { get; set; } }
拋出異常代碼:
throw new Exception("出錯了");
前端UI效果:
4. UserFriendlyException 友好異常
異常定義代碼:
/// <summary> /// 用戶友好異常 /// </summary> public class UserFriendlyException : Exception { public UserFriendlyException(string message) : base(message) { } }
在異常攔截關(guān)鍵代碼中,我們發(fā)現(xiàn)友好異常(UserFriendlyException)其實是返回了一個結(jié)果對象AjaxResult,
AjaxResult對象的定義:
/// <summary> /// 表示Ajax操作結(jié)果 /// </summary> public class AjaxResult { /// <summary> /// 獲取 Ajax操作結(jié)果類型 /// </summary> public ResultType type { get; set; } /// <summary> /// 獲取 Ajax操作結(jié)果編碼 /// </summary> public int errorcode { get; set; } /// <summary> /// 獲取 消息內(nèi)容 /// </summary> public string message { get; set; } /// <summary> /// 獲取 返回數(shù)據(jù) /// </summary> public object resultdata { get; set; } } /// <summary> /// 表示 ajax 操作結(jié)果類型的枚舉 /// </summary> public enum ResultType { /// <summary> /// 消息結(jié)果類型 /// </summary> info = 0, /// <summary> /// 成功結(jié)果類型 /// </summary> success = 1, /// <summary> /// 警告結(jié)果類型 /// </summary> warning = 2, /// <summary> /// 異常結(jié)果類型 /// </summary> error = 3 }
四、Ajax請求異常時處理
在異常攔截的關(guān)鍵代碼中,我們有看到,如果是ajax請求時,是執(zhí)行不同的邏輯,這是因為ajax的請求,不能直接通過MVC的路由跳轉(zhuǎn),在請求時必須返回結(jié)果內(nèi)容
然后在前端ajax的方法中,統(tǒng)一處理返回的錯誤,以下是我們項目中用到的ajax封裝,對異常錯誤,進(jìn)行了統(tǒng)一處理。
(function ($) { "use strict"; $.httpCode = { success: "1", fail: "3", }; // http 通信異常的時候調(diào)用此方法 $.httpErrorLog = function (msg) { console.log('=====>' + new Date().getTime() + '<====='); console.log(msg); }; // ajax請求錯誤處理 $.httpError = function (xhr, textStatus, errorThrown) { if (xhr.status == 401) { location.href = "/Error/Error401?errorUrl=" + xhr.responseText; } if (xhr.status == 404) { location.href = "/Error/Error404?errorUrl=" + xhr.responseText; } if (xhr.status == 500) { location.href = "/Error/Error500?data=" + xhr.responseText; } }; /* get請求方法(異步): * url地址, param參數(shù), callback回調(diào)函數(shù) beforeSend 請求之前回調(diào)函數(shù), complete 請求完成之后回調(diào)函數(shù) * 考慮到get請求一般將參數(shù)與url拼接一起傳遞,所以將param參數(shù)放置最后 * 返回AjaxResult結(jié)果對象 */ $.httpAsyncGet = function (url, callback, beforeSend, complete, param) { $.ajax({ url: url, data: param, type: "GET", dataType: "json", async: true, cache: false, success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, beforeSend: function () { if (!!beforeSend) beforeSend(); }, complete: function () { if (!!complete) complete(); } }); }; /* get請求方法(同步): * url地址,param參數(shù) * 返回實體數(shù)據(jù)對象 */ $.httpGet = function (url, param) { var res = {}; $.ajax({ url: url, data: param, type: "GET", dataType: "json", async: false, cache: false, success: function (data) { res = data; }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, }); return res; }; /* post請求方法(異步): * url地址, param參數(shù), callback回調(diào)函數(shù) beforeSend 請求之前回調(diào)函數(shù), complete 請求完成之后回調(diào)函數(shù) * 返回AjaxResult結(jié)果對象 */ $.httpAsyncPost = function (url, param, callback, beforeSend, complete) { $.ajax({ url: url, data: param, type: "POST", dataType: "json", async: true, cache: false, success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, beforeSend: function () { if (!!beforeSend) beforeSend(); }, complete: function () { if (!!complete) complete(); } }); }; /* post請求方法(同步): * url地址,param參數(shù), callback回調(diào)函數(shù) * 返回實體數(shù)據(jù)對象 */ $.httpPost = function (url, param, callback) { $.ajax({ url: url, data: param, type: "POST", dataType: "json", async: false, cache: false, success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, }); }, /* ajax異步封裝: * type 請求類型, url地址, param參數(shù), callback回調(diào)函數(shù) * 返回實體數(shù)據(jù)對象 */ $.httpAsync = function (type, url, param, callback) { $.ajax({ url: url, data: param, type: type, dataType: "json", async: true, cache: false, success: function (data) { if ($.isFunction(callback)) callback(data); }, error: function (XMLHttpRequest, textStatus, errorThrown) { $.httpError(XMLHttpRequest, textStatus, errorThrown); }, }); }; })(jQuery);
五、總結(jié)
至此,我們發(fā)現(xiàn)其實MVC的異常處理,真的很簡單,只需要在過濾器中全局注冊之后,然后重寫OnException的方法,實現(xiàn)邏輯即可。關(guān)鍵是在于項目中Ajax請求,需要用統(tǒng)一的封裝方法。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
ASP.NET中GridView、DataList、DataGrid三個數(shù)據(jù)控件foreach遍歷用法示例
這篇文章主要介紹了ASP.NET中GridView、DataList、DataGrid三個數(shù)據(jù)控件foreach遍歷用法,結(jié)合實例形式分析了GridView、DataList、DataGrid使用foreach及for語句進(jìn)行數(shù)據(jù)遍歷的具體使用方法,需要的朋友可以參考下2016-08-08asp.net下實現(xiàn)支持文件分塊多點異步上傳的 Web Services
asp.net下實現(xiàn)支持文件分塊多點異步上傳的 Web Services...2007-04-04JQuery實現(xiàn)Repeater無刷新批量刪除(附后臺asp.net源碼)
JQuery實現(xiàn)Repeater無刷新批量刪除(附后臺asp.net源碼) ,學(xué)習(xí)jquery的朋友可以參考下。2011-09-09asp.net 生成靜態(tài)時的過濾viewstate的實現(xiàn)方法
有時候我們在用asp.net生成靜態(tài)文件的時候,總會出現(xiàn)一些viewstate的字符,因為是靜態(tài)的不是aspx文件,所有沒必要留了,精簡代碼等原因,大家就需要看下面的方法了。2009-03-03