Hướng Dẫn Xác Thực JSON Schema: Xác Minh Cấu Trúc Dữ Liệu
Mọi ứng dụng nhận dữ liệu từ nguồn bên ngoài đều đối mặt với cùng một vấn đề cơ bản: tôi có thể tin tưởng dữ liệu này không? Cho dù là API request body, file cấu hình, queue message hay import dữ liệu, dữ liệu không đáng tin cậy phải được xác thực trước khi vào hệ thống. JSON Schema cung cấp cách chuẩn hóa, độc lập ngôn ngữ để định nghĩa dữ liệu hợp lệ trông như thế nào và xác thực dữ liệu đầu vào có tuân thủ định nghĩa đó không. Hướng dẫn này bao gồm mọi thứ từ viết Schema đầu tiên đến các mẫu nâng cao cho xác thực phức tạp, với ví dụ thực tế bạn có thể áp dụng ngay.
JSON Schema Là Gì?
JSON Schema là một tài liệu JSON mô tả cấu trúc và ràng buộc của các tài liệu JSON khác. Nó là từ vựng cho phép bạn chú thích và xác thực dữ liệu JSON, được chuẩn hóa bởi IETF. JSON Schema định nghĩa các quy tắc về những trường nào phải tồn tại, chúng phải thuộc kiểu gì, giá trị nào được chấp nhận và đối tượng và mảng nên được cấu trúc như thế nào.
Hãy nghĩ về JSON Schema như hợp đồng dữ liệu của bạn. Giống như database schema định nghĩa cột, kiểu và ràng buộc của bảng, JSON Schema định nghĩa thuộc tính, kiểu và ràng buộc của tài liệu JSON. Bất kỳ dữ liệu JSON nào thỏa mãn tất cả ràng buộc trong Schema được gọi là instance hợp lệ, trong khi dữ liệu vi phạm bất kỳ ràng buộc nào là không hợp lệ.
JSON Schema Không Phải Là Gì
- Không phải định dạng dữ liệu: JSON Schema không định nghĩa cách dữ liệu được tuần tự hóa hoặc truyền tải. Nó chỉ mô tả dữ liệu hợp lệ trông như thế nào.
- Không phải thay thế cho logic nghiệp vụ: Xác thực Schema kiểm tra tính đúng đắn cấu trúc (kiểu, định dạng, phạm vi). Các quy tắc nghiệp vụ phức tạp (như "người dùng không thể chuyển khoản vượt quá số dư") thuộc về code ứng dụng.
- Không phải database schema: Mặc dù khái niệm tương tự, JSON Schema xác thực tài liệu JSON, không phải bảng database. Tuy nhiên, bạn có thể kết hợp JSON Schema với ràng buộc database để phòng thủ theo chiều sâu.
Phiên Bản JSON Schema
JSON Schema đã phát triển qua nhiều bản draft, mỗi bản thêm tính năng và hoàn thiện từ vựng. Hiểu các phiên bản này giúp bạn chọn phiên bản phù hợp cho dự án và tránh vấn đề tương thích.
| Phiên Bản | $schema URI | Trạng Thái | Tính Năng Chính |
|---|---|---|---|
| Draft 2020-12 | https://json-schema.org/draft/2020-12/schema | Hiện tại | prefixItems, dynamicRef, hỗ trợ từ vựng |
| Draft 2019-09 | https://json-schema.org/draft/2019-09/schema | Ổn định | unevaluatedProperties, $recursiveRef |
| Draft 7 | http://json-schema.org/draft-07/schema# | Hỗ trợ rộng rãi | if/then/else, contentEncoding |
| Draft 6 | http://json-schema.org/draft-06/schema# | Cũ | propertyNames, contains |
| Draft 4 | http://json-schema.org/draft-04/schema# | Đã lỗi thời | Phiên bản được áp dụng rộng rãi đầu tiên |
Viết Schema Đầu Tiên
Hãy bắt đầu với một ví dụ đơn giản: Schema cho đối tượng người dùng chứa tên, email và tuổi.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/user.json",
"title": "User",
"description": "A user account in the system",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "The user's full name"
},
"email": {
"type": "string",
"format": "email",
"description": "The user's email address"
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150,
"description": "The user's age in years"
}
},
"required": ["name", "email"],
"additionalProperties": false
}Schema này khai báo rằng một người dùng hợp lệ phải là một đối tượng với name và email là thuộc tính chuỗi bắt buộc. Thuộc tính age là tùy chọn, nhưng nếu tồn tại phải là số nguyên từ 0 đến 150. additionalProperties: false ngăn chặn bất kỳ thuộc tính nào không được định nghĩa trong Schema, giúp phát hiện lỗi chính tả và trường không mong đợi.
Tham Khảo Từ Khóa Cốt Lõi
JSON Schema cung cấp từ vựng phong phú các từ khóa để định nghĩa ràng buộc. Dưới đây là các từ khóa quan trọng nhất được tổ chức theo danh mục.
Từ Khóa Kiểu
| Từ Khóa | Mô Tả | Ví Dụ |
|---|---|---|
| type | Kiểu dữ liệu mong đợi | "type": "string" hoặc "type": ["string", "null"] |
| enum | Danh sách giá trị được phép | "enum": ["active", "inactive", "pending"] |
| const | Phải bằng chính xác giá trị này | "const": "v2" |
Ràng Buộc Số
| Định Dạng | Mô Tả | Example |
|---|---|---|
| minimum | Giá trị tối thiểu (bao gồm) | "minimum": 0 |
| exclusiveMinimum | Giá trị tối thiểu (không bao gồm) | "exclusiveMinimum": 0 |
| maximum | Giá trị tối đa (bao gồm) | "maximum": 100 |
| exclusiveMaximum | Giá trị tối đa (không bao gồm) | "exclusiveMaximum": 100 |
| multipleOf | Phải là bội số của giá trị này | "multipleOf": 0.01 |
Ràng Buộc Chuỗi
| Keyword | Description | Example |
|---|---|---|
| minLength | Độ dài chuỗi tối thiểu | "minLength": 1 |
| maxLength | Độ dài chuỗi tối đa | "maxLength": 255 |
| pattern | Regex mà chuỗi phải khớp | "pattern": "^[A-Z]{2}\\d{4}$" |
| format | Định dạng ngữ nghĩa (email, uri, date-time, v.v.) | "format": "email" |
Ràng Buộc Đối Tượng
| Keyword | Description | Example |
|---|---|---|
| properties | Schema cho mỗi thuộc tính đã biết | "properties": {"name": {"type": "string"}} |
| required | Danh sách thuộc tính bắt buộc | "required": ["name", "email"] |
| additionalProperties | Có cho phép thuộc tính bổ sung không | "additionalProperties": false |
| minProperties | Số lượng thuộc tính tối thiểu | "minProperties": 1 |
| maxProperties | Số lượng thuộc tính tối đa | "maxProperties": 10 |
| patternProperties | Schema cho thuộc tính khớp regex | "patternProperties": {"^S_": {"type": "string"}} |
Ràng Buộc Mảng
| Keyword | Description | Example |
|---|---|---|
| items | Schema cho tất cả mục mảng | "items": {"type": "string"} |
| prefixItems | Schema cho mục theo vị trí (Draft 2020-12) | "prefixItems": [{"type": "string"}, {"type": "number"}] |
| minItems | Số lượng mục tối thiểu | "minItems": 1 |
| maxItems | Số lượng mục tối đa | "maxItems": 100 |
| uniqueItems | Tất cả mục phải duy nhất | "uniqueItems": true |
Mẫu Schema Phổ Biến
Schema trong thế giới thực thường yêu cầu các mẫu vượt ra ngoài kiểm tra kiểu đơn giản. Dưới đây là các mẫu được sử dụng phổ biến nhất.
Xác Thực Điều Kiện Với if/then/else
Sử dụng logic điều kiện để áp dụng các ràng buộc khác nhau dựa trên giá trị thuộc tính. Ví dụ, đối tượng thanh toán yêu cầu các trường khác nhau tùy theo phương thức thanh toán.
{
"type": "object",
"properties": {
"method": { "enum": ["credit_card", "bank_transfer"] },
"card_number": { "type": "string" },
"routing_number": { "type": "string" }
},
"required": ["method"],
"if": {
"properties": { "method": { "const": "credit_card" } }
},
"then": {
"required": ["card_number"]
},
"else": {
"required": ["routing_number"]
}
}Kết Hợp Với allOf, anyOf, oneOf
Từ khóa kết hợp cho phép bạn kết hợp Schema theo cách mạnh mẽ:
- allOf: Dữ liệu phải thỏa mãn tất cả Schema con. Dùng để kết hợp nhiều ràng buộc hoặc trộn các đoạn Schema có thể tái sử dụng.
- anyOf: Dữ liệu phải thỏa mãn ít nhất một Schema con. Dùng cho kiểu union nơi nhiều hình dạng đều chấp nhận được.
- oneOf: Dữ liệu phải thỏa mãn chính xác một Schema con. Dùng cho các lựa chọn loại trừ lẫn nhau.
{
"oneOf": [
{
"type": "object",
"properties": {
"type": { "const": "email" },
"address": { "type": "string", "format": "email" }
},
"required": ["type", "address"]
},
{
"type": "object",
"properties": {
"type": { "const": "phone" },
"number": { "type": "string", "pattern": "^\\+?[1-9]\\d{1,14}$" }
},
"required": ["type", "number"]
}
]
}Tái Sử Dụng Schema Với $ref
Từ khóa $ref cho phép bạn tham chiếu và tái sử dụng Schema, loại bỏ trùng lặp và giữ Schema có thể bảo trì. Bạn có thể tham chiếu Schema trong cùng tài liệu hoặc trong file bên ngoài.
{
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"customer": { "$ref": "#/$defs/address" },
"shipping": { "$ref": "#/$defs/address" },
"billing": { "$ref": "#/$defs/address" },
"items": {
"type": "array",
"items": { "$ref": "#/$defs/lineItem" },
"minItems": 1
}
},
"required": ["customer", "items"],
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^\\d{5}(-\\d{4})?$" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
},
"required": ["street", "city", "zip", "country"]
},
"lineItem": {
"type": "object",
"properties": {
"product": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 },
"price": { "type": "number", "exclusiveMinimum": 0 }
},
"required": ["product", "quantity", "price"]
}
}
}Kiểu Nullable
Trong JSON Schema Draft 2020-12, kiểu nullable được biểu diễn bằng mảng kiểu chứa "null":
{
"type": ["string", "null"],
"description": "An optional display name, or null if not set"
}Trong các draft cũ hơn, đặc tả OpenAPI sử dụng từ khóa nullable: true. Đối với JSON Schema tiêu chuẩn, luôn sử dụng phương pháp mảng kiểu.
Xác Thực Định Dạng
Từ khóa format cung cấp xác thực ngữ nghĩa vượt ra ngoài kiểm tra cấu trúc. Nó chỉ định chuỗi phải tuân thủ một định dạng được công nhận. Các định dạng được hỗ trợ phổ biến bao gồm:
| Format | Description | Example |
|---|---|---|
| Địa chỉ email | user@example.com | |
| uri | URI hợp lệ | https://example.com |
| date-time | Ngày giờ ISO 8601 | 2024-01-15T14:30:00Z |
| ipv4 | Địa chỉ IPv4 | 192.168.1.1 |
| hostname | Tên máy chủ | example.com |
time
| ISO 8601 time | 14:30:00Z |
ipv4
| IPv4 address | 192.168.1.1 |
ipv6
| IPv6 address | ::1 |
uuid
| Universally unique identifier | 550e8400-e29b-41d4-a716-446655440000 |
hostname
| Internet hostname | www.example.com |
format{ strict: true }{ validateFormats: true } to enforce format checking.
Always verify that your validator enforces format constraints.
Xác Thực Dữ Liệu JSON Theo Chương Trình
Xác thực Schema hữu ích nhất khi được tích hợp vào code ứng dụng. Dưới đây là ví dụ sử dụng thư viện phổ biến trong các ngôn ngữ khác nhau.
JavaScript Với Ajv
Ajv là trình xác thực JSON Schema được sử dụng rộng rãi nhất trong JavaScript. Nó hỗ trợ tất cả phiên bản draft và cung cấp hiệu suất xuất sắc thông qua JIT compilation của Schema.
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv();
addFormats(ajv);
const schema = {
type: 'object',
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0 }
},
required: ['name', 'email'],
additionalProperties: false
};
const validate = ajv.compile(schema);
const valid = validate({ name: 'Alice', email: 'alice@example.com', age: 30 });
if (!valid) {
console.log(validate.errors);
// [{ keyword: 'required', params: { missingProperty: 'email' }, ... }]
}Python Với jsonschema
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0}
},
"required": ["name", "email"],
"additionalProperties": False
}
try:
validate(instance={"name": "Alice", "email": "alice@example.com"}, schema=schema)
except ValidationError as e:
print(f"Validation failed: {e.message}")Xác Thực Dữ Liệu API Với JSON Schema
Một trong những ứng dụng giá trị nhất của JSON Schema là xác thực dữ liệu API request và response. Điều này đảm bảo API của bạn nhận đầu vào đúng định dạng và trả về dữ liệu theo định dạng mong đợi, phát hiện lỗi sớm và cung cấp thông báo lỗi rõ ràng cho client.
Express.js Middleware
import Ajv from 'ajv';
const ajv = new Ajv({ allErrors: true });
function validateBody(schema) {
const validate = ajv.compile(schema);
return (req, res, next) => {
if (!validate(req.body)) {
return res.status(400).json({
error: 'Validation failed',
details: validate.errors
});
}
next();
};
}
app.post('/api/users',
validateBody({
type: 'object',
properties: {
name: { type: 'string', minLength: 1, maxLength: 100 },
email: { type: 'string', format: 'email' },
role: { enum: ['admin', 'editor', 'viewer'] }
},
required: ['name', 'email'],
additionalProperties: false
}),
(req, res) => {
// req.body is guaranteed valid here
createUser(req.body);
}
);OpenAPI và JSON Schema
OpenAPI (trước đây là Swagger) sử dụng tập con của JSON Schema để định nghĩa API request và response Schema. Nếu bạn đã sử dụng OpenAPI, bạn có thể trích xuất Schema từ đặc tả API và sử dụng chúng cho xác thực runtime. Các công cụ như openapi-schema-validator và express-openapi-validator có thể tự động hóa quá trình này, đảm bảo tài liệu API và logic xác thực của bạn luôn đồng bộ.
Thực Hành Tốt Nhất
- Luôn đặt $schema: Bao gồm từ khóa $schema để khai báo phiên bản draft mà Schema của bạn sử dụng. Điều này ngăn ngừa nhập nhằng và đảm bảo trình xác thực diễn giải Schema của bạn đúng cách.
- Sử dụng $id làm định danh Schema: Từ khóa $id cung cấp định danh duy nhất cho Schema của bạn và là URI cơ sở để giải quyết tham chiếu $ref. Luôn đặt nó ngay cả cho Schema cục bộ.
- Đặt additionalProperties: false: Theo mặc định, JSON Schema cho phép bất kỳ thuộc tính bổ sung nào trong đối tượng. Đặt additionalProperties: false giúp phát hiện lỗi chính tả và trường không mong đợi, làm cho Schema của bạn nghiêm ngặt hơn và API dễ dự đoán hơn.
- Sử dụng $defs cho thành phần tái sử dụng: Định nghĩa Schema chung trong $defs và tham chiếu chúng bằng $ref. Điều này giảm trùng lặp và làm cho Schema dễ bảo trì hơn.
- Thêm mô tả: Mọi thuộc tính và Schema nên có trường description. Điều này vừa là tài liệu vừa giúp nhà phát triển khác hiểu mục đích và ràng buộc của từng trường.
- Xác thực tại ranh giới hệ thống: Áp dụng xác thực Schema tại rìa của hệ thống: API endpoint, message consumer, pipeline import dữ liệu và trình tải cấu hình. Đừng xác thực dữ liệu mà code của bạn đã tạo ra và tin tưởng.
- Bật chế độ nghiêm ngặt trong trình xác thực: Cấu hình trình xác thực từ chối từ khóa không xác định, thực thi xác thực định dạng và báo cáo tất cả lỗi thay vì dừng ở lỗi đầu tiên. Điều này phát hiện nhiều vấn đề hơn trong một lần xác thực.
- Phiên bản hóa Schema: Bao gồm phiên bản Schema trong $id URI (ví dụ: https://example.com/schemas/user/v2.json). Điều này cho phép bạn phát triển Schema mà không phá vỡ consumer hiện có.
- Kiểm thử Schema: Viết unit test cho Schema bao gồm ví dụ hợp lệ và không hợp lệ. Điều này đảm bảo Schema thực thi đúng ràng buộc bạn dự định và phát hiện regression khi sửa đổi Schema.
- Sử dụng format để xác thực ngữ nghĩa: Ưu tiên format: "email" thay vì mẫu regex phức tạp. Format dễ đọc hơn, dễ bảo trì hơn và được hưởng lợi từ logic xác thực đã được kiểm thử kỹ trong thư viện trình xác thực.
Cần xác thực dữ liệu JSON theo Schema? Hãy dùng thử công cụ xác thực JSON Schema trực tuyến miễn phí của chúng tôi. Dán Schema và dữ liệu của bạn để nhận kết quả xác thực tức thì với thông báo lỗi chi tiết.
Dùng Thử Trình Xác Thực JSON SchemaJSON FormatterCâu Hỏi Thường Gặp
JSON Schema là gì?
JSON Schema là tài liệu JSON mô tả cấu trúc và ràng buộc của các tài liệu JSON khác. Nó cho phép bạn định nghĩa trường bắt buộc, kiểu dữ liệu mong đợi, phạm vi giá trị, mẫu chuỗi và cấu trúc đối tượng lồng nhau. Bạn có thể sử dụng nó để xác thực dữ liệu đầu vào, tạo tài liệu và tự động tạo form interface.
Tôi nên sử dụng phiên bản JSON Schema nào?
Sử dụng JSON Schema Draft 2020-12 cho dự án mới. Đây là phiên bản ổn định mới nhất và được các thư viện xác thực chính như Ajv hỗ trợ. Nếu cần tương thích với công cụ cũ, Draft 7 cũng được hỗ trợ rộng rãi. Tránh Draft 4 và các phiên bản cũ hơn vì chúng sử dụng từ khóa lỗi thời và thiếu tính năng hiện đại.
JSON Schema khác TypeScript Interface như thế nào?
TypeScript interface chỉ cung cấp kiểm tra kiểu tại thời điểm biên dịch trong code TypeScript. JSON Schema cung cấp xác thực runtime trên nhiều ngôn ngữ lập trình, có thể xác thực dữ liệu từ bất kỳ nguồn nào (API request, file, database). Sử dụng TypeScript để có an toàn khi phát triển, sử dụng JSON Schema để xác thực dữ liệu runtime tại ranh giới hệ thống.
JSON Schema có thể xác thực API request body không?
Có, JSON Schema được sử dụng rộng rãi để xác thực API request và response. Các framework như Express (với express-json-validator), FastAPI và Spring Boot hỗ trợ nguyên bản hoặc qua middleware xác thực JSON Schema. Xác thực request body theo Schema đảm bảo dữ liệu đầu vào có cấu trúc đúng trước khi ứng dụng xử lý.
Từ khóa JSON Schema quan trọng nhất là gì?
Các từ khóa quan trọng nhất là: type (kiểu dữ liệu), properties (trường đối tượng), required (trường bắt buộc), items (Schema phần tử mảng), minimum/maximum (ranh giới số), minLength/maxLength (ranh giới chuỗi), pattern (regex chuỗi), enum (giá trị được phép) và $ref (tham chiếu để tái sử dụng Schema). Những từ khóa này bao phủ phần lớn nhu cầu xác thực.