為了將錯誤訊息呈現給使用者,要完成幾件事:
- 用 Filter 攔截 UserException
- 將錯誤訊息存到 TempData,之後呈現在畫面上
- 回到原本的畫面,需要產生 ViewResult
- 針對 Ajax 請求用 400 狀態回應,並將訊息放在 http content
.Net Framework MVC
using System; using System.Linq; using System.Text.RegularExpressions; using System.Web.Mvc; namespace XXXXX.Mvc.Filters { /// <summary>例外訊息過濾器</summary> public class ExceptionMessageActionFilter : ActionFilterAttribute { /// <summary></summary> public override void OnActionExecuting(ActionExecutingContext filterContext) { /* 自動將 Action 的 Parameters 中的 ViewModel 賦予給 ViewData * 不然要自己在 Action 的第一行寫 ViewData.Model = domain; */ string paramName = filterContext.ActionDescriptor.GetParameters() .OrderBy(x => Regex.IsMatch(x.ParameterType.ToString(), @"(ViewModel|Domain)\b") ? 0 : 1) .Select(x => x.ParameterName) .FirstOrDefault(); if (paramName != null) { filterContext.Controller.ViewData.Model = filterContext.ActionParameters[paramName]; } } /// <summary></summary> public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); /* 不處理子 Action */ if (filterContext.IsChildAction) { return; } /* 判斷 Exception 是否已經被處理了 */ if (filterContext.ExceptionHandled) { return; } /* 只針對 UserException 進行錯誤處理*/ var ex = filterContext.Exception as UserException; if (ex == null) { return; } /* 標記 Exception 已經被處理了,讓後續的 Filter 不用再處理 */ filterContext.ExceptionHandled = true; if (filterContext.HttpContext.Request.IsAjaxRequest()) { /* 對 Ajax 請求的處理 */ filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; filterContext.HttpContext.Response.StatusCode = 400; filterContext.Result = new ContentResult { Content = ex.Message }; } else if (ex is JwNoDataException) { /* 資料不存在的處理 */ filterContext.Controller.TempData["StatusError"] = ex.Message; filterContext.Result = new HttpNotFoundResult("[" + ex.Message + "]"); } else { /* 一般畫面的處理 */ filterContext.Controller.TempData["StatusError"] = ex.Message; filterContext.Result = new ViewResult { ViewData = filterContext.Controller.ViewData, TempData = filterContext.Controller.TempData }; } } } }
在 FilterConfig.cs 配置
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //... filters.Add(new ExceptionMessageActionFilter()); //... } }
Net core 3.1 MVC
using System; using System.Linq; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace Orion.Mvc.Filters { /// <summary>例外訊息過濾器</summary> public class ExceptionMessageActionFilter : ActionFilterAttribute { /// <summary></summary> public override void OnActionExecuting(ActionExecutingContext filterContext) { /* 自動將 Action 的 Arguments 中的 ViewModel 賦予給 ViewData * 不然要自己在 Action 的第一行寫 ViewData.Model = domain; */ var arguments = filterContext.ActionArguments.Values.Where(x => x != null); object model = arguments .OrderBy(x => Regex.IsMatch(x.GetType().Name, @"(ViewModel|Domain)\b") ? 0 : 1) .FirstOrDefault(); var controller = filterContext.Controller as Controller; controller.ViewData.Model = model; } /// <summary></summary> public override void OnActionExecuted(ActionExecutedContext filterContext) { /* 判斷 Exception 是否已經被處理了 */ base.OnActionExecuted(filterContext); if (filterContext.ExceptionHandled) { return; } /* 只針對 UserException 進行錯誤處理*/ var ex = filterContext.Exception as UserException; if (ex == null) { return; } /* 標記 Exception 已經被處理了,讓後續的 Filter 不用再處理 */ filterContext.ExceptionHandled = true; var controller = filterContext.Controller as Controller; var headers = filterContext.HttpContext.Request.Headers; bool isAjax = headers["X-Requested-With"] == "XMLHttpRequest"; if (isAjax) { /* 對 Ajax 請求的處理 */ filterContext.HttpContext.Response.StatusCode = 400; filterContext.Result = new ContentResult { StatusCode = 400, Content = ex.Message }; } else if (ex is UserNoDataException) { /* 資料不存在的處理 */ controller.TempData["StatusError"] = ex.Message; filterContext.HttpContext.Response.StatusCode = 404; } else { /* 一般畫面的處理 */ controller.TempData["StatusError"] = ex.Message; filterContext.Result = new ViewResult { ViewData = controller.ViewData, TempData = controller.TempData }; } } } }
在 Startup.cs 配置
public void ConfigureServices(IServiceCollection services) { //... IMvcBuilder mvcBuilder = services .AddMvc(options => { //... options.Filters.Add(new ExceptionMessageActionFilter()); //... }) .AddControllersAsServices() ; //... }
Net core 3.1 Razor Page
using System; using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; namespace XXXXX.Api.Filters { public class ExceptionMessagePageFilter : AbstractPageFilter { public override void OnPageHandlerExecuted(PageHandlerExecutedContext context) { if (context.ExceptionHandled) { return; } /* 判斷是否有指定的 Exception */ var ex = context.Exception; if (ex is UserException userEx) { handleUserException(context, userEx); return; } if (ex is HttpException httpEx) { handleHttpException(context, httpEx); return; } } private void handleUserException(PageHandlerExecutedContext context, UserException ex) { /* 只針對 PageModel 進行錯誤處理*/ var page = context.HandlerInstance as PageModel; if (page == null) { return; } /* 標記 Exception 已經被處理了,讓後續的 Filter 不用再處理 */ context.ExceptionHandled = true; var headers = filterContext.HttpContext.Request.Headers; bool isAjax = headers["X-Requested-With"] == "XMLHttpRequest"; if (isAjax) { /* 對 Ajax 請求的處理 */ context.HttpContext.Response.StatusCode = 400; context.Result = new ContentResult { StatusCode = 400, Content = ex.Message }; } else if (ex is UserNoDataException) { /* 資料不存在的處理 */ page.TempData["StatusError"] = ex.Message; context.HttpContext.Response.StatusCode = 404; } else { /* 一般畫面的處理 */ page.TempData["StatusError"] = ex.Message; context.Result = page.Page(); } } private void handleHttpException(PageHandlerExecutedContext context, HttpException ex) { context.ExceptionHandled = true; var headers = context.HttpContext.Request.Headers; bool isAjax = headers["X-Requested-With"] == "XMLHttpRequest"; if (isAjax) { context.HttpContext.Response.StatusCode = ex.StatusCode; context.Result = new ContentResult { StatusCode = ex.StatusCode, Content = ex.Message, }; } else { context.HttpContext.Response.StatusCode = ex.StatusCode; context.Result = new StatusCodeResult(ex.StatusCode); } } } }
在 Startup.cs 配置
public void ConfigureServices(IServiceCollection services) { //... IMvcBuilder mvcBuilder = services .AddRazorPages(options => { //... }) .AddMvcOptions(options => { //... options.Filters.Add(new ExceptionMessagePageFilter()); //... }) ; //... }
沒有留言:
張貼留言
你好!歡迎你在我的 Blog 上留下你寶貴的意見。