Appearance
HTTP Status Code
Status code là ngôn ngữ hợp đồng quan trọng nhất giữa API và mọi bên tiêu thụ nó (client, proxy, cache, monitoring tool) — chúng đọc status code để biết cách xử lý mà không cần đọc response body. Dùng sai/không nhất quán khiến việc debug và tích hợp tốn kém hơn nhiều so với vẻ ngoài "chỉ là 1 con số".
5 nhóm status code
| Nhóm | Ý nghĩa | Ghi chú |
|---|---|---|
1xx | Thông tin tạm thời | Hiếm khi cần quan tâm ở tầng ứng dụng |
2xx | Thành công | 200 OK, 201 Created, 204 No Content |
3xx | Chuyển hướng | 301/302 redirect, 304 Not Modified (cache) |
4xx | Lỗi phía client | Request sai/thiếu quyền/không tồn tại — client cần sửa request, không nên retry y nguyên |
5xx | Lỗi phía server | Server gặp sự cố khi xử lý — client có thể retry sau (nhất là với idempotency key) |
Một API CRUD thiết kế tốt thường chỉ cần khoảng 15 status code trong toàn bộ hệ thống — không cần cố dùng hết bảng mã HTTP.
Bảng dùng nhanh cho CRUD
| Tình huống | Status code |
|---|---|
| Đọc thành công | 200 OK |
| Tạo mới thành công | 201 Created |
| Thao tác thành công, không có nội dung trả về (ví dụ xoá) | 204 No Content |
| Request sai định dạng/thiếu trường bắt buộc | 400 Bad Request |
| Chưa xác thực (không có/token sai) | 401 Unauthorized |
| Đã xác thực nhưng không đủ quyền | 403 Forbidden |
| Resource không tồn tại | 404 Not Found |
| Xung đột dữ liệu (ví dụ trùng email khi đăng ký) | 409 Conflict |
| Dữ liệu đúng định dạng nhưng vi phạm business rule | 422 Unprocessable Entity |
| Gọi quá số lần cho phép trong khoảng thời gian (rate limit) | 429 Too Many Requests |
| Lỗi không lường trước ở server | 500 Internal Server Error |
Rate limiting — vì sao cần và cách báo cho client biết
Rate limiting là giới hạn số request 1 client (theo user, theo API key, hoặc theo IP) được gọi trong 1 khoảng thời gian — bảo vệ server khỏi bị quá tải (vô tình do bug client gọi lặp, hoặc cố ý do tấn công/brute-force). Khi vượt giới hạn, server trả 429 Too Many Requests, thường kèm header Retry-After (số giây client nên đợi trước khi gọi lại). Nên áp dụng rate limiting ít nhất cho các endpoint nhạy cảm (đăng nhập, gửi OTP...) ngay từ đầu, không đợi tới khi bị lạm dụng mới thêm.
Ví dụ minh hoạ — 1 luồng tạo resource hoàn chỉnh
Request:
POST /users
Content-Type: application/json
{
"email": "user@example.com",
"name": "Nguyen Van A"
}Response thành công:
HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/123
{
"id": "123",
"email": "user@example.com",
"name": "Nguyen Van A",
"createdAt": "2026-08-09T10:00:00Z"
}Response khi validate lỗi:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Du lieu khong hop le",
"details": [
{ "field": "email", "message": "Email khong dung dinh dang" }
]
}
}Chú ý: status code (201, 400) đi kèm 1 body JSON mô tả chi tiết — client dựa vào status code để biết loại kết quả (thành công/lỗi), rồi đọc body để biết chi tiết (dữ liệu tạo ra, hoặc lý do lỗi cụ thể).
Sai lầm thường gặp
- Lỗi validate dữ liệu trả về
5xxthay vì4xx— validate sai là lỗi của request (client), không phải lỗi server. Trả5xxcho lỗi validate khiến client hiểu nhầm là "cứ retry sẽ hết lỗi", trong khi thực tế phải sửa dữ liệu gửi lên. - Nhầm
401và403:401= chưa biết bạn là ai (thiếu/sai thông tin xác thực);403= đã biết bạn là ai nhưng không đủ quyền làm việc này. Dùng sai khiến client không biết nên "đăng nhập lại" hay "xin quyền". - Lộ thông tin nhạy cảm trong response lỗi — không nên trả nguyên stack trace, câu lệnh SQL, hay chi tiết implementation trong message lỗi trả về client; chỉ nên trả thông điệp chung chung, log chi tiết ở phía server.
- Format lỗi không nhất quán giữa các endpoint — mỗi endpoint tự bịa 1 kiểu JSON lỗi khác nhau khiến client phải xử lý riêng từng trường hợp. Chuẩn khuyến nghị hiện nay là RFC 9457 Problem Details — một format JSON thống nhất cho lỗi API (nội dung chi tiết về chuẩn hoá lỗi và Exception Filter sẽ nằm ở note "Exception Handling" riêng, vì đây là chủ đề tuần kế tiếp trong plan).