Dotnet Core WebApi Exception Handle Middleware

簡單在Dotnet Core WebApi中實作全域錯誤處理,使用Middleware進行處理,並且擷取Request帶入的資料

前言

近期支援其他的開發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的專案範例。

參考連結

All rights reserved,未經允許不得隨意轉載
Built with Hugo
Theme Stack designed by Jimmy