前言
近期支援其他的開發team,剛好遇到一個專案沒有進行錯誤處理,卻又準備上PRD, 在開發期間develop環境又常常噴莫名其妙的400跟500錯誤,下定決定先搞一個錯誤處理, 不然上線感覺會炸裂XD。
思考作法
錯誤處理,其實有很多方式可以實踐,最簡單又不影響原先程式的做法,一種是Exception Filter, 不過能夠攔截的錯誤有一些限制,再出現400、500錯誤的世界似乎是不夠使用的, 所以就變成另一種做法Exception Middleware,來實現我想要的錯誤攔截。
作法
建立ExceptionHandleMiddleware
首先建立ExceptionHandleMiddleware,廢話不多說直接上code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
public class ExceptionHandleMiddleware { private readonly RequestDelegate _next; private readonly ILogger<ExceptionHandleMiddleware> _logger; public ExceptionHandleMiddleware(RequestDelegate next, ILogger<ExceptionHandleMiddleware> logger) { _next = next; _logger = logger; } /// <summary> /// 任務調用 /// </summary> /// <param name="context">HTTP 的上下文</param> /// <returns></returns> public async Task Invoke(HttpContext context) { // 確保 HTTP Request 可以多次讀取 context.Request.EnableBuffering(); try { await _next(context); } catch (Exception exception) { await HandleExceptionAsync(context, exception); } } private Task HandleExceptionAsync(HttpContext context, Exception exception) { var path = $"{context.Request.Path}"; var method = $"{context.Request.Method}"; var controllerName = $"{context.Request.RouteValues["controller"]}"; var actionName = $"{context.Request.RouteValues["action"]}"; var env = $"{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}"; var body = ReadRequestBody(context); _logger.LogError("{now} | [{env}]({method}):{path} | {controllerName}-{actionName} | {message} | Data:{body}", $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}", env, method, path, controllerName, actionName, exception.StackTrace, body); context.Response.ContentType = "application/json"; context.Response.StatusCode = StatusCodes.Status500InternalServerError; return context.Response.WriteAsync($"Catch Exception from Middleware : {exception.Message}"); } private static string ReadRequestBody(HttpContext context) { using var reader = new StreamReader(context.Request.Body, Encoding.UTF8); // 將 HTTP Request 的 Stream 起始位置歸零 context.Request.Body.Seek(0, SeekOrigin.Begin); var body = reader.ReadToEndAsync().ConfigureAwait(false).GetAwaiter().GetResult(); // 將 HTTP Request 的 Stream 起始位置歸零 context.Request.Body.Seek(0, SeekOrigin.Begin); return body; } }
說明
簡單說明一下,上面code處理了些什麼,Middleware本身像是簽核過程一樣,一層一層往上簽,一個呼叫一個, 所以當錯誤攔截的Middleware擺在最上層的時候,基本上就是包山包海的狀態,蠻符合我的需求的,所以在裡面包一個簡單的try catch, 進行錯誤的處理,在這裡的錯誤處理取得了大部分所需的資訊,包含傳入的Request內容,這個部分應該是蠻多錯誤檢查需要使用的資料, 後面會放上source code的專案範例。