Thời lượng: 25 phút
Level: Intermediate
Yêu cầu: Đã hoàn thành bài về Validation và Error Codes.
Mục tiêu bài học
Sau bài học này, bạn sẽ:
- Xây dựng được Middleware xử lý lỗi tập trung cho ứng dụng Backend.
- Đảm bảo thông tin nhạy cảm không bị rò rỉ (leak) ra ngoài khi xảy ra lỗi hệ thống.
- Biết cách log lỗi chi tiết ở server trong khi vẫn trả về lỗi gọn gàng cho client.
Nội dung chính
1. Tại sao cần Global Handler?
Thông thường, nếu bạn không bắt lỗi (try-catch), khi có lỗi phát sinh (VD: DB die, Code crash), framework sẽ tự động trả về lỗi thô sơ của nó (VD: Stack trace dài dằng dặc, lộ DB connection string...).
Lợi ích của Global Handler:
- Nhất quán: Mọi lỗi đều trả về đúng format Envelope đã học ở Module 2.
- Bảo mật: Giấu đi các thông tin kỹ thuật nhạy cảm.
- Tiện lợi: Developer chỉ cần
throwlỗi, không cần lo việc format response ở từng controller.
2. Quy trình xử lý lỗi 4 bước
Khi một lỗi xảy ra ở bất cứ đâu trong code, nó sẽ trôi về Global Handler:
- Nhận diện (Identify): Kiểm tra xem đây là lỗi ĐÃ BIẾT (Business Error) hay lỗi KHÔNG MONG MUỐN (System Crash/500).
- Ghi nhật ký (Logging): Ghi lại toàn bộ Stack trace, chi tiết lỗi vào hệ thống log (VD: Winston, ELK, Datadog) kèm theo
trace_id. - Lọc thông tin (Sanitize): Nếu là lỗi 500, thay thế thông báo kỹ thuật bằng một câu chung chung ("Internal Server Error") để bảo mật.
- Phản hồi (Respond): Đóng gói vào Envelope JSON và trả về cho Client cùng HTTP Status phù hợp.
3. Ví dụ Code Pattern (Node.js/Express)
// Middleware xử lý lỗi tập trung
function globalErrorHandler(err, req, res, next) {
// 1. Log lỗi chi tiết ở Server (Dành cho Dev)
console.error(`[ERROR] TraceID: ${req.traceId}`, err);
// 2. Xác định cấu trúc trả về
const success = false;
let statusCode = err.statusCode || 500;
let errorCode = err.code || 'INTERNAL_ERROR';
let message = err.message || 'Một lỗi bất ngờ đã xảy ra';
// 3. Bảo mật: Nếu là lỗi server chưa biết, giấu message chi tiết
if (statusCode === 500) {
message = 'Lỗi hệ thống, vui lòng thử lại sau';
}
// 4. Trả về đúng chuẩn Design System
res.status(statusCode).json({
success,
data: null,
error: {
code: errorCode,
message: message,
details: err.details || []
},
meta: {
trace_id: req.traceId
}
});
}Nguyên tắc then chốt
"Log everything internally, expose only what is necessary externally."
Hệ thống log của bạn có thể chứa 10 trang stack trace để bạn debug, nhưng Client chỉ nên nhận được 3 dòng JSON gọn gàng.
Thực hành
Bài tập 1: Xây dựng Custom Error Class
Mục tiêu: Tạo ra các lớp lỗi kế thừa từ Error chuẩn để dễ dàng ném lỗi kèm theo Metadata.
Yêu cầu:
- Tạo class
AppErrorchứastatusCodevàerrorCode. - Tạo class
NotFoundErrorkế thừa từAppErrorvới status mặc định là 404.
Xem đáp án mẫu
class AppError extends Error {
constructor(public statusCode: number, public errorCode: string, message: string) {
super(message);
}
}
class NotFoundError extends AppError {
constructor(message = 'Resource không tồn tại') {
super(404, 'NOT_FOUND', message);
}
}
// Cách dùng trong Controller:
// throw new NotFoundError('Không tìm thấy đơn hàng này');Tình huống thực tế
Scenario 1: Lỗi từ thư viện bên thứ 3
Bối cảnh:
Bạn dùng thư viện kết nối Database. Khi DB sập, nó ném ra lỗi: Error: Connect timeout at 10.0.0.5:5432 after 30s. Stack: ...
Vấn đề: Nếu trả về nguyên văn, hacker sẽ biết địa chỉ IP nội bộ và loại database bạn đang dùng.
Giải quyết:
Trong Global Handler, bạn phải kiểm tra: "Nếu lỗi này không phải là instance của AppError của tôi, thì mặc định nó là 500 và trả về message an toàn."
Câu hỏi phỏng vấn
Senior Level
- Q: Tại sao ta nên gán một
trace_id(hoặcrequest_id) vào mọi Error Response? A:- Để hỗ trợ việc Support Người dùng: Khi user báo lỗi kèm theo ID này, dev có thể tra lại chính xác dòng log liên quan trong hàng triệu dòng log của server.
- Để debug trong Microservices: Giúp theo vết yêu cầu đi qua nhiều service khác nhau.
Tóm tắt
Những điều cần nhớ
- Global Handler giúp API luôn trả về một format duy nhất.
- Bảo mật thông tin bằng cách lọc bỏ chi tiết lỗi 500.
- Log chi tiết ở server kèm theo Trace ID.
Key Takeaways
- Safety: Không bao giờ để lộ Stack trace cho Client.
- Developer Experience: Giúp việc ném lỗi (throw error) trở nên nhẹ nhàng và nhất quán.
Bước tiếp theo
🎉 Chúc mừng! Bạn đã hoàn thành Module 4: Validation & Errors.
Giờ bạn đã nắm vững:
- Cách validate dữ liệu chặt chẽ.
- Cách quản lý mã lỗi tập trung.
- Cách xây dựng hệ thống xử lý lỗi an toàn và chuyên nghiệp.
Module tiếp theo: Module 5: Pagination & Filtering - Học cách chuẩn hóa việc lấy danh sách dữ liệu lớn, sắp xếp và tìm kiếm.