Xử lý lỗi tập trung
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Database mất kết nối, code ném exception mà không ai bắt, client chỉ nhận 500
không rõ lý do. Bọc try/catch vào từng action thì code lặp lại khắp nơi.
Bài này bắt mọi lỗi không lường trước ở một chỗ, còn lỗi cụ thể xử lý được
thì vẫn dùng try/catch như bài Exception.
Khái niệm
🛡️ Xử lý lỗi tập trung: bắt mọi exception chưa được xử lý ở một chỗ duy nhất trong pipeline, rồi trả về response lỗi thống nhất.
📄 ProblemDetails: định dạng JSON chuẩn để mô tả lỗi trong HTTP API, gồm các trường như title, status, detail.
Ví dụ
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddProblemDetails();
builder.Services
.AddExceptionHandler<AppExceptionHandler>();
var app = builder.Build();
app.UseExceptionHandler();
app.MapControllers();
app.Run();
public class AppExceptionHandler : IExceptionHandler
{
private readonly ILogger<AppExceptionHandler>
_logger;
public AppExceptionHandler(
ILogger<AppExceptionHandler> logger)
{
_logger = logger;
}
public async ValueTask<bool> TryHandleAsync(
HttpContext context,
Exception exception,
CancellationToken ct)
{
_logger.LogError(
exception, "Lỗi chưa được xử lý");
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(
new ProblemDetails
{
Status = 500,
Title = "Đã có lỗi xảy ra",
},
ct);
return true;
}
}IExceptionHandlerlà interface để viết bộ xử lý lỗi.TryHandleAsyncđược gọi khi có exception chưa ai bắt.ValueTask<bool>dùng nhưTask<bool>.- Bộ xử lý ghi log đầy đủ exception cho team, còn client chỉ nhận một câu chung chung.
- Trả
truenghĩa là lỗi đã được xử lý xong. AddExceptionHandlerđăng ký bộ xử lý,UseExceptionHandler()thêm nó vào pipeline.UseExceptionHandler()là một middleware, đặt đầu pipeline để bọc các bước sau.AddProblemDetails()đăng ký dịch vụ tạo ProblemDetails. Thiếu dòng này,UseExceptionHandler()không tham số làm ứng dụng dừng ngay lúc khởi động.ctdùng để huỷ việc ghi response khi client ngắt kết nối, ở đây chỉ cần truyền tiếp.
Đường đi của một exception chưa ai bắt:
Thử ngay
Thêm một action cố tình ném lỗi vào project:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/test")]
public class TestController : ControllerBase
{
[HttpGet("boom")]
public string Boom() =>
throw new InvalidOperationException(
"Mất kết nối tới kho ở Hà Nội");
}=> throw ... cho method ném exception ngay thay vì trả về giá trị. Chạy
server rồi gọi curl -i http://localhost:5000/api/test/boom.
Đoán trước khi chạy: câu "Mất kết nối tới kho ở Hà Nội" có xuất hiện trong response mà client nhận không?
Xem kết quả
HTTP/1.1 500 Internal Server Error
Content-Type: application/json; charset=utf-8
{"title":"Đã có lỗi xảy ra","status":500}Không. Câu đó chỉ nằm trong log của server, kèm stack trace đầy đủ. Client chỉ thấy thông báo chung, không biết gì về cấu trúc bên trong hệ thống.
Lỗi hay gặp
Bọc try/catch vào từng action. Cùng một đoạn xử lý lỗi bị chép lại ở
khắp nơi, sửa một chỗ dễ quên chỗ khác.
// SAI — mọi action đều lặp lại khối này
using Microsoft.AspNetCore.Mvc;
public class OrdersController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult Get(int id)
{
try
{
return Ok(id);
}
catch (Exception)
{
return StatusCode(500);
}
}
}// ĐÚNG — lỗi để bộ xử lý tập trung bắt
using Microsoft.AspNetCore.Mvc;
public class OrdersController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult Get(int id)
{
return Ok(id);
}
}Trả thẳng exception.Message hay stack trace cho client. Thông báo lỗi
có thể chứa tên bảng, đường dẫn file, chuỗi kết nối. Chỉ ghi chúng vào log.
Tóm tắt
- Bắt mọi exception chưa xử lý ở một chỗ bằng
IExceptionHandler. - Đăng ký bằng
AddExceptionHandler, bật bằngUseExceptionHandler(). - Ghi log đầy đủ cho team, trả
ProblemDetailsngắn gọn cho client. - Không bọc
try/catchvào từng action, không lộ chi tiết lỗi ra ngoài.
Bình luận
Đang tải bình luận…