註: 本文中的例外處理指的是處理 controller 中拋出的例外,並將例外轉換為特定的回應,用以簡化每個 Controller 在執行期間要處理的回應。而非使用中介軟體(Middleware) 捕捉全域例外。
假設我們將處理的邏輯放在 Service 層,在處理資料的時候往往就會面對到處理錯誤資料很麻煩的問題。例如在 Controller 接收到 id ,要取得使用者資料,但是將 id 傳入 Service 層後才發現該筆 id 是錯誤的,無法取回資料。就只能回傳 null ,回到外面的 Controller 再確認是否為空,若空就回傳 NotFound(404)。
假設有 100 個 API ,這樣麻煩的事情就要做非常多次,且如果是一次查詢多筆 id ,Service 層要告訴 Controller 是哪個 id 不存在也非常麻煩,可能還要使用 out 多個 failIds 之類的 array 才有辦法判斷。
先建立一個傳輸錯誤訊息的物件:
建立一個可以存放 ErrorMessageDto 的 Exception ,假設這個 Exception 專門用來表示 key 沒有找到的例外和儲存 ErrorMessageDto :
實現 IOrderedFilter 介面必須要有 Order 屬性,此屬性代表篩選器執行的順序,越小越優先。例如 AuthorizationFilter 的 Order 為 -200 ,若要比 AuthorizationFilter 還要優先,則將 Order 指定為 -201 即可。
上面使用 switch 是因為我們可能會再建立多個自訂例外,故直接使用 switch。
此時只要開啟下面的網頁即可顯示藉由篩選器回應的 404 訊息(記得替換 port)
參考資料:
Microsoft.learn - Handle errors in ASP.NET Core web APIs
假設我們將處理的邏輯放在 Service 層,在處理資料的時候往往就會面對到處理錯誤資料很麻煩的問題。例如在 Controller 接收到 id ,要取得使用者資料,但是將 id 傳入 Service 層後才發現該筆 id 是錯誤的,無法取回資料。就只能回傳 null ,回到外面的 Controller 再確認是否為空,若空就回傳 NotFound(404)。
假設有 100 個 API ,這樣麻煩的事情就要做非常多次,且如果是一次查詢多筆 id ,Service 層要告訴 Controller 是哪個 id 不存在也非常麻煩,可能還要使用 out 多個 failIds 之類的 array 才有辦法判斷。
自訂例外
雖然有許多既有的例外類別可以使用,例如 KeyNotFoundException, ArgumentException ,不過不容易傳遞我們想要的錯誤訊息。先建立一個傳輸錯誤訊息的物件:
public class ErrorMessageDto
{
public string Field { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
}
建立一個可以存放 ErrorMessageDto 的 Exception ,假設這個 Exception 專門用來表示 key 沒有找到的例外和儲存 ErrorMessageDto :
/// <summary>
/// 主鍵無效例外
/// </summary>
public class ApiKeyNotFoundException : Exception
{
public ErrorMessageDto ErrorMessage { get; set; }
public ApiKeyNotFoundException(ErrorMessageDto errorMessage)
{
ErrorMessage = errorMessage;
Console.WriteLine("ApiKeyNotFoundException: " + errorMessage);
}
}
建立自訂例外篩選器
public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
{
public int Order => 1000;
public void OnActionExecuting(ActionExecutingContext context)
{
}
public void OnActionExecuted(ActionExecutedContext context)
{
switch (context.Exception)
{
case ApiKeyNotFoundException apiKeyNotFoundException:
context.Result = new NotFoundObjectResult(apiKeyNotFoundException.ErrorMessage);
context.ExceptionHandled = true; // 標記此異常已被處理
break;
}
}
}
實現 IOrderedFilter 介面必須要有 Order 屬性,此屬性代表篩選器執行的順序,越小越優先。例如 AuthorizationFilter 的 Order 為 -200 ,若要比 AuthorizationFilter 還要優先,則將 Order 指定為 -201 即可。
上面使用 switch 是因為我們可能會再建立多個自訂例外,故直接使用 switch。
將篩選器加入至控制器
在 Program.cs 中的 var app = builder.Build(); 前加入以下程式碼:
builder.Services.AddControllers(options =>
{
options.Filters.Add<HttpResponseExceptionFilter>();
});
建立會拋出自訂例外的 Acrion
在自動產生的 HomeController 中增加以下必定會拋出例外的測試用 Action:
[HttpGet("/user/{id:int}")]
public IActionResult User(int id)
{
throw new ApiKeyNotFoundException(new ErrorMessageDto
{
Field = nameof(id),
Message = $"The user id {id} is not found."
});
}
此時只要開啟下面的網頁即可顯示藉由篩選器回應的 404 訊息(記得替換 port)
https://localhost:7110/user/5
參考資料:
Microsoft.learn - Handle errors in ASP.NET Core web APIs
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com