ASP.NET MVC 異常Exception攔截

1、前言

因爲客戶端的環境不一致,有可能會形成咱們預計不到的異常錯誤,因此在項目中,友好的異常信息提示,是很是重要的。asp.net mvc中實現異常屬性攔截也很是簡單,只須要繼承另外一個類(System.Web.Mvc.FilterAttribute)和一個接口(System.Web.Mvc.IExceptionFilter),實現接口裏面OnException方法,或者直接繼承Mvc 提供的類System.Web.Mvc.HandleErrorAttribute前端

2、實現關鍵邏輯

繼承System.Web.Mvc.HandleErrorAttribute,重寫了OnException方法,主要實現邏輯代碼以下:ajax

  1. public class HandlerErrorAttribute : HandleErrorAttribute
  2. {
  3.     /// <summary>
  4.     /// 控制器方法中出現異常,會調用該方法捕獲異常
  5.     /// </summary>
  6.     /// <param name="context">提供使用</param>
  7.     public override void OnException(ExceptionContext context)
  8.     {
  9.         WriteLog(context);
  10.         base.OnException(context);
  11.         context.ExceptionHandled = true;
  12.         if (context.Exception is UserFriendlyException)
  13.         {
  14.             context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
  15.             context.Result = new ContentResult { Content = new AjaxResult { type = ResultType.error, message = context.Exception.Message }.ToJson() };
  16.         }
  17.         else if (context.Exception is NoAuthorizeException)
  18.         {
  19.             context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
  20.             if (!context.HttpContext.Request.IsAjaxRequest())
  21.             {
  22.                 context.HttpContext.Response.RedirectToRoute("Default", new { controller = "Error", action = "Error401", errorUrl = context.HttpContext.Request.RawUrl });
  23.             }
  24.             else
  25.             {
  26.                 context.Result = new ContentResult { Content = context.HttpContext.Request.RawUrl };
  27.             }
  28.         }
  29.         else
  30.         {
  31.              context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
  32.              ExceptionMessage error = new ExceptionMessage(context.Exception);
  33.              var s = error.ToJson();
  34.              if (!context.HttpContext.Request.IsAjaxRequest())
  35.              {
  36.                  context.HttpContext.Response.RedirectToRoute("Default", new { controller = "Error", action = "Error500", data = WebHelper.UrlEncode(s) });
  37.              }
  38.              else
  39.              {
  40.                  context.Result = new ContentResult { Content = WebHelper.UrlEncode(s) };
  41.              }
  42.         }
  43.     }
  44.  
  45.     /// <summary>
  46.     /// 寫入日誌(log4net)
  47.     /// </summary>
  48.     /// <param name="context">提供使用</param>
  49.     private void WriteLog(ExceptionContext context)
  50.     {
  51.         if (context == null)
  52.             return;
  53.         if (context.Exception is NoAuthorizeException || context.Exception is UserFriendlyException)
  54.         {
  55.             //友好錯誤提示,未受權錯誤提示,記錄警告日誌
  56.             LogHelper.Warn(context.Exception.Message);
  57.         }
  58.         else
  59.         {
  60.             //異常錯誤,
  61.             LogHelper.Error(context.Exception);
  62.  
  63.             ////TODO :寫入錯誤日誌到數據庫
  64.         }
  65.     }
  66. }

MVC 過濾器全局註冊異常攔截: 數據庫

  1. public class FilterConfig
  2.     {
  3.         public static void RegisterGlobalFilters(GlobalFilterCollection filters)
  4.         {
  5.             filters.Add(new HandlerErrorAttribute());
  6.         }
  7.     }

 

咱們看到,context.Exception 分爲3種:UserFriendlyException,NoAuthorizeException 或 Exception;UserFriendlyException 是指友好異常,前端友好提示錯誤信息。NoAuthorizeException 爲401未受權異常,當頁面未被受權訪問時,返回該異常,並攜帶有未受權的路徑地址。其餘異常統一返回500錯誤,並攜帶異常信息。 json

 

3、異常處理

 

1.401 未受權錯誤

 

異常定義代碼:瀏覽器

  1. /// <summary>
  2. /// 沒有被受權的異常
  3. /// </summary>
  4. public class NoAuthorizeException : Exception
  5. {
  6.     public NoAuthorizeException(string message)
  7.         : base(message)
  8.     {
  9.     }
  10. }

拋出異常代碼:服務器

  1. throw new NoAuthorizeException("未受權");

前端UI效果:mvc

 

2.404 未找到頁面錯誤

MVC的404異常處理,有幾種方式,咱們採用了在Global.asax全局請求函數中處理, 請查看如下代碼 app

  1. protected void Application_EndRequest()
  2.      {
  3.          if (Context.Response.StatusCode == 404)
  4.          {
  5.              bool isAjax = new HttpRequestWrapper(Context.Request).IsAjaxRequest();
  6.              if (isAjax)
  7.              {
  8.                  Response.Clear();
  9.                  Response.Write(Context.Request.RawUrl);
  10.              }
  11.              else
  12.              {
  13.                  Response.RedirectToRoute("Default", new { controller = "Error", action = "Error404", errorUrl = Context.Request.RawUrl });
  14.              }
  15.          }
  16.      }

前端UI效果:asp.net

3.500服務器內部錯誤

 

500異常錯誤拋出的異常信息對象定義:異步

  1. /// <summary>
  2. /// 異常錯誤信息
  3. /// </summary>
  4. [Serializable]
  5. public class ExceptionMessage
  6. {
  7.     public ExceptionMessage()
  8.     {
  9.     }
  10.  
  11.     /// <summary>
  12.     /// 構造函數
  13.     /// 默認顯示異常頁面
  14.     /// </summary>
  15.     /// <param name="ex">異常對象</param>
  16.     public ExceptionMessage(Exception ex)
  17.         :this(ex, true)
  18.     {
  19.  
  20.     }
  21.     /// <summary>
  22.     /// 構造函數
  23.     /// </summary>
  24.     /// <param name="ex">異常對象</param>
  25.     /// <param name="isShowException">是否顯示異常頁面</param>
  26.     public ExceptionMessage(Exception ex, bool isShowException)
  27.     {
  28.         MsgType = ex.GetType().Name;
  29.         Message = ex.InnerException != null ? ex.InnerException.Message : ex.Message;
  30.         StackTrace = ex.StackTrace.Length > 300 ? ex.StackTrace.Substring(0, 300) : ex.StackTrace;
  31.         Source = ex.Source;
  32.         Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  33.         Assembly = ex.TargetSite.Module.Assembly.FullName;
  34.         Method = ex.TargetSite.Name;
  35.  
  36.         ShowException = isShowException;
  37.         var request = HttpContext.Current.Request;
  38.         IP = Net.Ip;
  39.         UserAgent = request.UserAgent;
  40.         Path = request.Path;
  41.         HttpMethod = request.HttpMethod;
  42.     }
  43.     /// <summary>
  44.     /// 消息類型
  45.     /// </summary>
  46.     public string MsgType { get; set; }
  47.  
  48.     /// <summary>
  49.     /// 消息內容
  50.     /// </summary>
  51.     public string Message { get; set; }
  52.  
  53.     /// <summary>
  54.     /// 請求路徑
  55.     /// </summary>
  56.     public string Path { get; set; }
  57.  
  58.     /// <summary>
  59.     /// 程序集名稱
  60.     /// </summary>
  61.     public string Assembly { get; set; }
  62.  
  63.     /// <summary>
  64.     /// 異常參數
  65.     /// </summary>
  66.     public string ActionArguments { get; set; }
  67.  
  68.     /// <summary>
  69.     /// 請求類型
  70.     /// </summary>
  71.     public string HttpMethod { get; set; }
  72.  
  73.     /// <summary>
  74.     /// 異常堆棧
  75.     /// </summary>
  76.     public string StackTrace { get; set; }
  77.  
  78.     /// <summary>
  79.     /// 異常源
  80.     /// </summary>
  81.     public string Source { get; set; }
  82.  
  83.     /// <summary>
  84.     /// 服務器IP 端口
  85.     /// </summary>
  86.     public string IP { get; set; }
  87.  
  88.     /// <summary>
  89.     /// 客戶端瀏覽器標識
  90.     /// </summary>
  91.     public string UserAgent { get; set; }
  92.  
  93.  
  94.     /// <summary>
  95.     /// 是否顯示異常界面
  96.     /// </summary>
  97.     public bool ShowException { get; set; }
  98.  
  99.     /// <summary>
  100.     /// 異常發生時間
  101.     /// </summary>
  102.     public string Time { get; set; }
  103.  
  104.     /// <summary>
  105.     /// 異常發生方法
  106.     /// </summary>
  107.     public string Method { get; set; }
  108. }

拋出異常代碼:

  1. throw new Exception("出錯了");

前端UI效果:

4. UserFriendlyException 友好異常

 

異常定義代碼:

  1. /// <summary>
  2. /// 用戶友好異常
  3. /// </summary>
  4. public class UserFriendlyException : Exception
  5. {
  6.     public UserFriendlyException(string message)
  7.         : base(message)
  8.     {
  9.     }
  10. }

在異常攔截關鍵代碼中,咱們發現友好異常(UserFriendlyException)實際上是返回了一個結果對象AjaxResult,

AjaxResult對象的定義:

  1. /// <summary>
  2.     /// 表示Ajax操做結果
  3.     /// </summary>
  4.     public class AjaxResult
  5.     {
  6.         /// <summary>
  7.         /// 獲取 Ajax操做結果類型
  8.         /// </summary>
  9.         public ResultType type { get; set; }
  10.  
  11.         /// <summary>
  12.         /// 獲取 Ajax操做結果編碼
  13.         /// </summary>
  14.         public int errorcode { get; set; }
  15.  
  16.         /// <summary>
  17.         /// 獲取 消息內容
  18.         /// </summary>
  19.         public string message { get; set; }
  20.  
  21.         /// <summary>
  22.         /// 獲取 返回數據
  23.         /// </summary>
  24.         public object resultdata { get; set; }
  25.     }
  26.     /// <summary>
  27.     /// 表示 ajax 操做結果類型的枚舉
  28.     /// </summary>
  29.     public enum ResultType
  30.     {
  31.         /// <summary>
  32.         /// 消息結果類型
  33.         /// </summary>
  34.         info = 0,
  35.  
  36.         /// <summary>
  37.         /// 成功結果類型
  38.         /// </summary>
  39.         success = 1,
  40.  
  41.         /// <summary>
  42.         /// 警告結果類型
  43.         /// </summary>
  44.         warning = 2,
  45.  
  46.         /// <summary>
  47.         /// 異常結果類型
  48.         /// </summary>
  49.         error = 3
  50.     }

 

4、Ajax請求異常時處理

 

在異常攔截的關鍵代碼中,咱們有看到,若是是ajax請求時,是執行不一樣的邏輯,這是由於ajax的請求,不能直接經過MVC的路由跳轉,在請求時必須返回結果內容

而後在前端ajax的方法中,統一處理返回的錯誤,如下是咱們項目中用到的ajax封裝,對異常錯誤,進行了統一處理。

  1. (function ($) {
  2.     "use strict";
  3.  
  4.     $.httpCode = {
  5.         success: "1",
  6.         fail: "3",
  7.     };
  8.     // http 通訊異常的時候調用此方法
  9.     $.httpErrorLog = function (msg) {
  10.         console.log('=====>' + new Date().getTime() + '<=====');
  11.         console.log(msg);
  12.     };
  13.  
  14.     // ajax請求錯誤處理
  15.     $.httpError = function (xhr, textStatus, errorThrown) {
  16.  
  17.         if (xhr.status == 401) {
  18.             location.href = "/Error/Error401?errorUrl=" + xhr.responseText;
  19.         }
  20.  
  21.         if (xhr.status == 404) {
  22.             location.href = "/Error/Error404?errorUrl=" + xhr.responseText;
  23.         }
  24.  
  25.         if (xhr.status == 500) {
  26.             location.href = "/Error/Error500?data=" + xhr.responseText;
  27.         }
  28.     };
  29.  
  30.     /* get請求方法(異步):
  31.     * url地址, param參數, callback回調函數 beforeSend 請求以前回調函數, complete 請求完成以後回調函數
  32.     * 考慮到get請求通常將參數與url拼接一塊兒傳遞,因此將param參數放置最後
  33.     * 返回AjaxResult結果對象
  34.     */
  35.     $.httpAsyncGet = function (url, callback, beforeSend, complete, param) {
  36.         $.ajax({
  37.             url: url,
  38.             data: param,
  39.             type: "GET",
  40.             dataType: "json",
  41.             async: true,
  42.             cache: false,
  43.             success: function (data) {
  44.                 if ($.isFunction(callback)) callback(data);
  45.             },
  46.             error: function (XMLHttpRequest, textStatus, errorThrown) {
  47.                 $.httpError(XMLHttpRequest, textStatus, errorThrown);
  48.             },
  49.             beforeSend: function () {
  50.                 if (!!beforeSend) beforeSend();
  51.             },
  52.             complete: function () {
  53.                 if (!!complete) complete();
  54.             }
  55.         });
  56.     };
  57.  
  58.     /* get請求方法(同步):
  59.     * url地址,param參數
  60.     * 返回實體數據對象
  61.     */
  62.     $.httpGet = function (url, param) {
  63.         var res = {};
  64.         $.ajax({
  65.             url: url,
  66.             data: param,
  67.             type: "GET",
  68.             dataType: "json",
  69.             async: false,
  70.             cache: false,
  71.             success: function (data) {
  72.                 res = data;
  73.             },
  74.             error: function (XMLHttpRequest, textStatus, errorThrown) {
  75.                 $.httpError(XMLHttpRequest, textStatus, errorThrown);
  76.             },
  77.         });
  78.         return res;
  79.     };
  80.  
  81.     /* post請求方法(異步):
  82.     * url地址, param參數, callback回調函數 beforeSend 請求以前回調函數, complete 請求完成以後回調函數
  83.     * 返回AjaxResult結果對象
  84.     */
  85.     $.httpAsyncPost = function (url, param, callback, beforeSend, complete) {
  86.         $.ajax({
  87.             url: url,
  88.             data: param,
  89.             type: "POST",
  90.             dataType: "json",
  91.             async: true,
  92.             cache: false,
  93.             success: function (data) {
  94.                 if ($.isFunction(callback)) callback(data);
  95.             },
  96.             error: function (XMLHttpRequest, textStatus, errorThrown) {
  97.                 $.httpError(XMLHttpRequest, textStatus, errorThrown);
  98.             },
  99.             beforeSend: function () {
  100.                 if (!!beforeSend) beforeSend();
  101.             },
  102.             complete: function () {
  103.                 if (!!complete) complete();
  104.             }
  105.         });
  106.     };
  107.  
  108.     /* post請求方法(同步):
  109.     * url地址,param參數, callback回調函數
  110.     * 返回實體數據對象
  111.     */
  112.     $.httpPost = function (url, param, callback) {
  113.         $.ajax({
  114.             url: url,
  115.             data: param,
  116.             type: "POST",
  117.             dataType: "json",
  118.             async: false,
  119.             cache: false,
  120.             success: function (data) {
  121.                 if ($.isFunction(callback)) callback(data);
  122.             },
  123.             error: function (XMLHttpRequest, textStatus, errorThrown) {
  124.                 $.httpError(XMLHttpRequest, textStatus, errorThrown);
  125.             },
  126.         });
  127.     },
  128.  
  129.     /* ajax異步封裝:
  130.     * type 請求類型, url地址, param參數, callback回調函數
  131.     * 返回實體數據對象
  132.     */
  133.     $.httpAsync = function (type, url, param, callback) {
  134.         $.ajax({
  135.             url: url,
  136.             data: param,
  137.             type: type,
  138.             dataType: "json",
  139.             async: true,
  140.             cache: false,
  141.             success: function (data) {
  142.                 if ($.isFunction(callback)) callback(data);
  143.             },
  144.             error: function (XMLHttpRequest, textStatus, errorThrown) {
  145.                 $.httpError(XMLHttpRequest, textStatus, errorThrown);
  146.             },
  147.         });
  148.     };
  149. })(jQuery);

 

5、總結

至此,咱們發現其實MVC的異常處理,真的很簡單,只須要在過濾器中全局註冊以後,而後重寫OnException的方法,實現邏輯便可。關鍵是在於項目中Ajax請求,須要用統一的封裝方法。

相關文章
相關標籤/搜索