Thời lượng: 25 phút
Level: Intermediate
Yêu cầu: Đã hoàn thành Modules 1-3.
Mục tiêu bài học
Sau bài học này, bạn sẽ:
- Hiểu tầm quan trọng của việc validate dữ liệu ngay tại cửa ngõ API.
- Thiết kế được cấu trúc thông báo lỗi chi tiết đến từng trường (Field-level errors).
- Phân biệt được các loại lỗi validation: Type, Format, và Business logic.
Nội dung chính
1. Tại sao cần Validation nghiêm ngặt?
Trong một Design System, validation không chỉ là để bảo mật mà còn là để hướng dẫn người dùng. Nếu API chỉ trả về một câu chung chung là "Invalid Data", người dùng (và Dev Frontend) sẽ rất bối rối không biết mình sai ở đâu.
Ba lớp của validation:
| Lớp | Loại lỗi | Ví dụ |
|---|---|---|
| Lớp 1 (Type) | Sai kiểu dữ liệu | Gửi "abc" vào trường age (vốn là số). |
| Lớp 2 (Format) | Sai định dạng | Email thiếu dấu @, Số điện thoại quá ngắn. |
| Lớp 3 (Logic) | Vi phạm quy tắc nghiệp vụ | Số tiền đấu giá thấp hơn giá sàn hiện tại. |
2. Field-level Errors: Nguyên tắc vàng
Nguyên tắc: Lỗi phải máy-đọc-được (machine-readable) để Client có thể tự động highlight đúng ô nhập liệu bị sai.
Cấu trúc lỗi không tốt:
{
"success": false,
"error": "Email hoặc mật khẩu không đúng định dạng"
}Cấu trúc chuẩn Design System:
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Dữ liệu đầu vào không hợp lệ",
"details": [
{
"field": "email",
"rule": "format",
"message": "Email không đúng định dạng @example.com"
},
{
"field": "age",
"rule": "min",
"message": "Bạn phải trên 18 tuổi để tham gia",
"meta": { "min": 18, "actual": 15 }
}
]
}
}3. Quy trình thực hiện (Pipeline)
- Schema Validation: Kiểm tra Type và Format bằng các thư viện như Joi, Zod hoặc Class-validator. Bước này nên diễn ra ngay tại Middleware trước khi vào Controller.
- Business Validation: Kiểm tra trong Service/Domain logic (VD: Check trong DB xem email có bị trùng không).
- Error Collection: Tổng hợp tất cả các lỗi và trả về một lần duy nhất (Batching errors) thay vì báo từng lỗi một.
Nguyên tắc then chốt
"Đừng bao giờ tin tưởng bất cứ thứ gì đến từ Client."
Mọi dữ liệu đầu vào đều tiềm ẩn nguy cơ XSS, SQL Injection hoặc đơn giản là làm crash hệ thống vì sai kiểu dữ liệu. Validation là lớp phòng thủ đầu tiên và quan trọng nhất.
Thực hành
Bài tập 1: Thiết kế Error Response cho Form Đăng ký
Bối cảnh: User gửi một request đăng ký với các lỗi sau:
- Username: để trống.
- Password: chỉ có 3 ký tự (yêu cầu tối thiểu 8).
- Email: không có dấu @.
Yêu cầu:
- Viết một JSON response theo đúng chuẩn Envelope và Field-level errors đã học.
Xem đáp án mẫu
{
"success": false,
"error": {
"code": "VALIDATION_FAILED",
"message": "Vui lòng kiểm tra lại thông tin nhập liệu",
"details": [
{ "field": "username", "rule": "required", "message": "Tên đăng nhập không được để trống" },
{ "field": "password", "rule": "min_length", "message": "Mật khẩu phải từ 8 ký tự trở lên", "meta": { "min": 8 } },
{ "field": "email", "rule": "format", "message": "Email sai định dạng" }
]
}
}Tình huống thực tế
Scenario 1: Trùng lặp Email
Bối cảnh: Khi user nhấn "Đăng ký", hệ thống chạy Schema validation thì thấy mọi thứ OK (Email đúng format). Nhưng khi vào đến database, hệ thống mới phát hiện Email này đã có người dùng.
Câu hỏi:
- Lỗi này nên trả về ở bước nào?
fieldtrongdetailsnên để là gì để Frontend có thể báo lỗi ngay tại ô Email?
Xem phân tích
Phân tích:
- Đây là lỗi Business Logic. Nó nên được ném ra từ Service layer.
- Tuy là lỗi DB, nhưng nó vẫn liên quan trực tiếp đến một ô nhập liệu. Vì vậy vẫn nên trả về trong mảng
detailsvớifield: "email". Điều này cực kỳ quan trọng để UX tốt hơn là một câu báo lỗi chung chung ở đầu trang.
Câu hỏi phỏng vấn
Junior/Mid Level
- Q: Tại sao nên dùng Middleware để validate thay vì viết code check trong Controller?
A:
- Tách biệt trách nhiệm (SoC): Controller chỉ nên xử lý luồng, không nên chứa đống logic check string empty hay regex.
- Tái sử dụng: Một Schema có thể dùng cho nhiều API khác nhau.
- Fail-fast: Chặn đứng yêu cầu sai trái ngay từ cửa ngõ trước khi tốn tài nguyên xử lý ở các lớp sâu hơn.
Tóm tắt
Những điều cần nhớ
- Luôn trả về lỗi chi tiết đến từng trường (Field-level).
- Sử dụng mảng
detailsđể chứa danh sách các lỗi. - Phân tách lỗi format (cú pháp) và lỗi nghiệp vụ (logic).
Key Takeaways
- Machine-readable: Quan trọng nhất là Client phải tự động xử lý được lỗi.
- Standardization: Mọi API trong Design System phải trả về lỗi cùng một cấu trúc.
Bước tiếp theo
Trong bài tiếp theo, bạn sẽ học về Lesson 4.2: Error Codes Registry - Cách quản lý và đặt tên hàng trăm mã lỗi một cách khoa học.