為了將錯誤訊息呈現給使用者,要完成幾件事:
- 用 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());
- //...
- })
- ;
- //...
- }