Mục lục

Lesson 4.1: Input Validation

Xây dựng cơ chế kiểm soát dữ liệu đầu vào nghiêm ngặt để bảo vệ hệ thống và cung cấp phản hồi chi tiết cho người dùng.

Validation
Security
Schema

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ớpLoại lỗiVí dụ
Lớp 1 (Type)Sai kiểu dữ liệuGửi "abc" vào trường age (vốn là số).
Lớp 2 (Format)Sai định dạngEmail 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:

json:
{
  "success": false,
  "error": "Email hoặc mật khẩu không đúng định dạng"
}

Cấu trúc chuẩn Design System:

json:
{
  "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)

  1. 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.
  2. Business Validation: Kiểm tra trong Service/Domain logic (VD: Check trong DB xem email có bị trùng không).
  3. 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
json:
{
  "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:

  1. Lỗi này nên trả về ở bước nào?
  2. field trong details nê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 details với field: "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

  1. 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

  1. Machine-readable: Quan trọng nhất là Client phải tự động xử lý được lỗi.
  2. 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.

Tiếp tục bài 4.2 →

Quảng cáo
mdhorizontal