.NET6中全局異常處理
微信公眾號(hào):趣編程ACE
關(guān)注可了解每日一更的.NET日常實(shí)戰(zhàn)開(kāi)發(fā)技巧,歡迎公眾號(hào)留言開(kāi)發(fā) 獲取源碼;
.NET6中全局異常處理
異常處理是我們?cè)诔绦蜷_(kāi)發(fā)中不可或缺的一環(huán),下文我將會(huì)結(jié)合程序Sample講解如何在.NET6中有效處理異常。
Try-Ctach 塊包裹
自定義異常中間件
Try-Catch 塊
Try-Catch 是最基本的異常處理方法,下面我們看下例子。
創(chuàng)建一個(gè)基于.net6的Asp.Net Core Web Api項(xiàng)目
1using?ExceptionHandling.Services;
2using?Microsoft.AspNetCore.Mvc;
3namespace?ExceptionHandling.Controllers;
4
5[ApiController]
6[Route("api/[controller]")]
7public?class?UserController?:?Controller
8{
9????private?readonly?IUserService?_userService;
10????private?readonly?ILogger?_logger;
11
12????///?
13????///?依賴注入?IUserService?ILogger
14????///?
15????///?
16????///?
17????public?UserController(IUserService?userService,?ILogger?logger )
18????{
19????????_userService?=?userService;
20????????_logger?=?logger;
21????}
22
23????[HttpGet]
24????public?IActionResult?GetUsers()
25????{
26
27???????try
28???????{
29???????????_logger.LogInformation("Get?User?Details");
30
31???????????var?result?=?_userService.GetUsers();
32???????????if(result.Count==0)
33????????????throw?new?ApplicationException("Get?User?failed");?//?此處拋出一個(gè)獲取用戶出錯(cuò)異常
34
35???????????return?Ok(result);
36???????}
37???????catch?(System.Exception?e)
38???????{
39???????????_logger.LogError(e.Message);
40???????????return?BadRequest("獲取失敗");?//?返回給前端
41???????}
42????}
43}
我們?cè)赩sCode里面按照一個(gè)Postman插件PostCode 調(diào)用上面接口https://localhost:7218/api/User


通過(guò)結(jié)果可知,當(dāng)我們沒(méi)有獲取到用戶的時(shí)候,代碼將會(huì)拋出一個(gè)Get User failed的異常(見(jiàn)上圖)。對(duì)于初學(xué)者來(lái)說(shuō),這是最常見(jiàn)最基礎(chǔ)的方法,但是這個(gè)方法對(duì)于大項(xiàng)目來(lái)說(shuō)也有一個(gè)缺點(diǎn)。
如果項(xiàng)目中有許多控制器和動(dòng)作方法,然后我們需要對(duì)每一個(gè)動(dòng)作方法都使用try-catch,那么在這種情況下,用try-catch就很累贅,也會(huì)增加代碼行數(shù)。此時(shí)就需要自定義一個(gè)處理全局異常的中間件啦!
使用這個(gè)方法的好處就是我們可以在一個(gè)地方捕獲未處理的異常,而不需要在每個(gè)動(dòng)作方法中使用try-catch。
自定義中間件處理異常
在根目錄下創(chuàng)建一個(gè)Middlewares文件夾,新建一個(gè)名為ExceptionHandlingMiddleware.cs類
1using?System.Net;
2using?System.Text.Json;
3using?ExceptionHandling.Models.Responses;
4
5namespace?ExceptionHandling.Middlewares;
6
7public?class?ExceptionHandlingMiddleware
8{
9????private?readonly?RequestDelegate?_next;??//?用來(lái)處理上下文請(qǐng)求??
10????private?readonly?ILogger?_logger;
11????public?ExceptionHandlingMiddleware(RequestDelegate?next,?ILogger?logger )
12????{
13????????_next?=?next;
14????????_logger?=?logger;
15????}
16
17????public?async?Task?InvokeAsync(HttpContext?httpContext)
18????{
19????????try
20????????{
21????????????await?_next(httpContext);?//要么在中間件中處理,要么被傳遞到下一個(gè)中間件中去
22????????}
23????????catch?(Exception?ex)
24????????{
25????????????await?HandleExceptionAsync(httpContext,?ex);?//?捕獲異常了?在HandleExceptionAsync中處理
26????????}
27????}
28????private?async?Task?HandleExceptionAsync(HttpContext?context,?Exception?exception)
29????{
30????????context.Response.ContentType?=?"application/json";??//?返回json?類型
31????????var?response?=?context.Response;
32
33????????var?errorResponse?=?new?ErrorResponse
34????????{
35????????????Success?=?false
36????????};??//?自定義的異常錯(cuò)誤信息類型
37????????switch?(exception)
38????????{
39????????????case?ApplicationException?ex:
40????????????????if?(ex.Message.Contains("Invalid?token"))
41????????????????{
42????????????????????response.StatusCode?=?(int)?HttpStatusCode.Forbidden;
43????????????????????errorResponse.Message?=?ex.Message;
44????????????????????break;
45????????????????}
46????????????????response.StatusCode?=?(int)?HttpStatusCode.BadRequest;
47????????????????errorResponse.Message?=?ex.Message;
48????????????????break;
49????????????case?KeyNotFoundException?ex:
50????????????????response.StatusCode?=?(int)?HttpStatusCode.NotFound;
51????????????????errorResponse.Message?=?ex.Message;
52????????????????break;
53????????????default:
54????????????????response.StatusCode?=?(int)?HttpStatusCode.InternalServerError;
55????????????????errorResponse.Message?=?"Internal?Server?errors.?Check?Logs!";
56????????????????break;
57????????}
58????????_logger.LogError(exception.Message);
59????????var?result?=?JsonSerializer.Serialize(errorResponse);
60????????await?context.Response.WriteAsync(result);
61????}
62}
這就是我們自定義的中間件,在ExceptionHandlingMiddleware中,我們首先通過(guò)依賴注入ILogger和RequestDelegate服務(wù)。委托類型_next用來(lái)處理上下文請(qǐng)求,要么將上下文放在中間件中處理,要么傳遞到下個(gè)中間鏈里的下一個(gè)中間件中去。
如果我們的請(qǐng)求發(fā)生異常,那么就會(huì)執(zhí)行HandleExceptionAsync這個(gè)方法。這個(gè)方法里面會(huì)根據(jù)異常類型來(lái)返回不同的狀態(tài)碼并且記錄到日志中去,不需要返回給調(diào)用的客戶端,然后我們就可以通過(guò)檢查日志來(lái)發(fā)現(xiàn)異常信息。
我們?cè)?strong style="font-size: inherit;color: inherit;line-height: inherit;">Program.cs中添加自定義異常
1?app.UseMiddleware<ExceptionHandlingMiddleware>();
接著我們修改下控制器里GetUsers()這個(gè)方法,去掉try-catch,直接拋出異常
1[HttpGet]
2????public?IActionResult?GetUsers()
3????{
4
5??????_logger.LogInformation("Get?User?Details");
6
7???????????var?result?=?_userService.GetUsers();
8???????????if(result.Count==0)
9????????????throw?new?KeyNotFoundException("Get?User?failed");?//?此處拋出一個(gè)KeyNotFoundException異常
10
11???????????return?Ok(result);
12????}
通過(guò)調(diào)試我們可以發(fā)現(xiàn),當(dāng)發(fā)生異常的時(shí)候,程序?qū)?huì)執(zhí)行到HandleExceptionAsync()方法中去,接著根據(jù)類型KeyNotFoundException 返回404的狀態(tài)碼和異常信息。

