MVC異常過濾器

來源:互聯網
上載者:User

標籤:還需要   protect   異常處理   自訂   filters   inner   ble   strong   div   

 MVC過濾器

  • 一般的過濾器執行順序
  1. IAuthorizationFilter->OnAuthorization(授權)
  2. IActionFilter          ->OnActionExecuting(行為)
  3. Action
  4. IActionFilter          ->OnActionExecuted(行為) 
  5. IResultFilter          ->OnResultExecuting(結果)
  6. View
  7. IResultFilter          ->OnResultExecuted(結果)
  8. *IExceptionFilter    ->OnException(異常),此方法並不在以上的順序執行中,有異常發生時即會執行,有點類似於中斷
  • 當同時在Controller和Action中都設定了過濾器後,執行順序一般是由外到裡,即“全域”->“控制器”->“行為”
  1. Controller->IAuthorizationFilter->OnAuthorization
  2. Action     ->IAuthorizationFilter->OnAuthorization
  3. Controller->IActionFilter          ->OnActionExecuting
  4. Action     ->IActionFilter          ->OnActionExecuting
  5. Action
  6. Action     ->IActionFilter          ->OnActionExecuted
  7. Controller->IActionFilter          ->OnActionExecuted
  8. Controller->IResultFilter          ->OnResultExecuting
  9. Action     ->IResultFilter          ->OnActionExecuting
  10. Action     ->IResultFilter          ->OnActionExecuted
  11. Controller->IResultFilter          ->OnActionExecuted
  • 因為異常是從裡往外拋,因次異常的處理順序則剛好相反,一般是由裡到外,即“行為”->“控制器”->“全域”
  1. Action     ->IExceptionFilter->OnException
  2. Controller->IExceptionFilter->OnException 

 
系統內建的異常處理
我們習慣使用的過濾器,要麼是為Action加上Attribute,要麼就是為Controller加上Attribute。上面所說的全域過濾器是怎麼回事呢?先看看Gloabal裡的代碼:
protected void Application_Start(){ 
//註冊Area AreaRegistration.RegisterAllAreas(); 
//註冊過濾器 RegisterGlobalFilters(GlobalFilters.Filters); 
//註冊路由 RegisterRoutes(RouteTable.Routes);
}
 public static void RegisterGlobalFilters(GlobalFilterCollection filters)

    filters.Add(new HandleErrorAttribute()); 


由上可知,在應用程式啟動的時候就已經註冊了全域過濾器,HandleErrorAttribute就是系統內建的異常過濾器。在這註冊的全域過濾器,可以不用到每個Controller或者是每個Action去聲明,直接作用於全域了,即可以捕捉整個網站的所有異常。看看它的源碼是怎麼處理異常的:

public virtual void OnException(ExceptionContext filterContext)        {            if (filterContext == null)            {                throw new ArgumentNullException("filterContext");            }            if (!filterContext.IsChildAction && (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))            {                Exception innerException = filterContext.Exception;                if ((new HttpException(null, innerException).GetHttpCode() == 500) && this.ExceptionType.IsInstanceOfType(innerException))                {                    string controllerName = (string) filterContext.RouteData.Values["controller"];                    string actionName = (string) filterContext.RouteData.Values["action"];                    HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);                    ViewResult result = new ViewResult {                        ViewName = this.View,                        MasterName = this.Master,                        ViewData = new ViewDataDictionary<HandleErrorInfo>(model),                        TempData = filterContext.Controller.TempData                    };                    filterContext.Result = result;                    filterContext.ExceptionHandled = true;                    filterContext.HttpContext.Response.Clear();                    filterContext.HttpContext.Response.StatusCode = 500;                    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;                }            }        }

 


HandleErrorAttribute的異常處理邏輯裡,產生了一個HandleErrorInfo類的Model,並設定返回的結果為一個新產生的ViewResult。這個視圖預設的ViewName是Error,對應於Share檔案夾裡的Error視圖。而內建的Error視圖沒有用到HandleErrorInfo的Model,因此公開的資訊也不是很多,可以根據具體的需求改造一下。例如:
   

@model HandleErrorInfo<br /><div class="container"> <div class="alert alert-error"> <h4> Exception:</h4> <br /> <p> There was a <b>@Model.Exception.GetType().Name</b> while rendering <b>@Model.ControllerName</b>‘s<b>@Model.ActionName</b> action.</p> <p> @Model.Exception.Message </p> </div>  <div class="alert"> <h4> Stack trace:</h4> <br /> <pre>@Model.Exception.StackTrace</pre> </div> </div>

 



這個過濾器要能起效,還需要在設定檔中配置一下:<customErrors mode="On" />

自訂的異常統一處理

在實現異常的統一處理之前,先來明確一下需求:

  1. 網站所有頁面在異常發生後,均需要記錄異常日誌,並轉向錯誤提示頁面(異常內容的詳略程度由具體需求決定)
  2. 所有返回JSON資料的非同步請求,不但需要記錄異常日誌,而且需要向用戶端返回JSON格式的錯誤資訊提示,而不是轉向錯誤提示頁面(非同步請求也不可能轉向錯誤提示頁面)
  3. 採用AOP思想,將異常處理解耦
  4. 盡量精簡聲明Attribute的重複代碼
  [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]    public class JsonExceptionAttribute : HandleErrorAttribute    {        public override void OnException(ExceptionContext filterContext)        {            if (!filterContext.ExceptionHandled)            {                                                //返回異常JSON                filterContext.Result = new JsonResult                {                    Data = new { Success = false, Message = filterContext.Exception.Message }                };            }        }    }

 

說明:需要注意的是,不需要調用base.OnException,否則會跳過LogExceptionAttribute先執行HandleErrorAttribute的處理邏輯,從而返回結果不再是JsonResult,而是ViewResult,用戶端也就無法處理非JSON的結果了。
這裡也不需要設定filterContext.ExceptionHandled = true,否則在LogExceptionAttribute處理時,因為 !filterContext.ExceptionHandled 的判斷條件,LogExceptionAttribute的邏輯不會執行,也就不會記錄異常日誌了。 

 

   [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]    public class LogExceptionAttribute : HandleErrorAttribute    {        public override void OnException(ExceptionContext filterContext)        {            if (!filterContext.ExceptionHandled)            {                string controllerName = (string)filterContext.RouteData.Values["controller"];                string actionName = (string)filterContext.RouteData.Values["action"];                string msgTemplate = "在執行 controller[{0}] 的 action[{1}] 時產生異常";                LogManager.GetLogger("LogExceptionAttribute").Error(string.Format(msgTemplate, controllerName, actionName), filterContext.Exception);            }             if (filterContext.Result is JsonResult)            {                //當結果為json時,設定異常已處理                filterContext.ExceptionHandled = true;            }            else            {                //否則調用原始設定                base.OnException(filterContext);            }        }    } 

 

修改全域過濾器:

 public static void RegisterGlobalFilters(GlobalFilterCollection filters) {      filters.Add(new HandleError2LogAttribute());//全域的日誌異常過濾器      //filters.Add(new HandleErrorAttribute());}

調用樣本:
[HttpPost][JsonException]public JsonResult Add(string ip, int port){        ...  //處理邏輯        return Json(new { Success = true, Message = "添加成功" });} 

 

MVC異常過濾器

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.