handling-validation-errors
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHandling Validation Errors in z-schema
z-schema 验证错误处理
z-schema reports validation errors as objects containing a array of . This skill covers inspecting, filtering, mapping, and presenting these errors.
ValidateError.detailsSchemaErrorDetailz-schema 将验证错误以 对象的形式返回,该对象包含 类型的 数组。本教程涵盖这些错误的检查、过滤、映射和展示方法。
ValidateErrorSchemaErrorDetail.detailsError structure
错误结构
typescript
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';ValidateErrorError| Property | Type | Description |
|---|---|---|
| | Always |
| | Summary message |
| | All individual errors |
Each :
SchemaErrorDetail| Field | Type | Description |
|---|---|---|
| | Human-readable text, e.g. |
| | Machine-readable code, e.g. |
| | Values filling the message template placeholders |
| | JSON Pointer to the failing value ( |
| | Schema keyword that caused the error ( |
| | Sub-errors from combinators ( |
| | Path within the schema to the constraint |
| | Schema ID if present |
| | Schema |
| | Schema |
typescript
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';ValidateErrorError| 属性 | 类型 | 描述 |
|---|---|---|
| | 固定为 |
| | 摘要消息 |
| | 所有单个错误列表 |
每个 包含的字段:
SchemaErrorDetail| 字段 | 类型 | 描述 |
|---|---|---|
| | 人类可读的提示文本,例如 |
| | 机器可读的错误码,例如 |
| | 填充消息模板占位符的具体值 |
| | 指向出错值的JSON Pointer( |
| | 触发错误的Schema关键字( |
| | 来自组合器( |
| | 约束条件在Schema内部的路径 |
| | Schema ID(如果存在) |
| | Schema的 |
| | Schema的 |
Capturing errors
捕获错误
Try/catch (default mode)
Try/catch(默认模式)
typescript
import ZSchema from 'z-schema';
const validator = ZSchema.create();
try {
validator.validate(data, schema);
} catch (err) {
if (err instanceof ValidateError) {
for (const detail of err.details) {
console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
}
}
}typescript
import ZSchema from 'z-schema';
const validator = ZSchema.create();
try {
validator.validate(data, schema);
} catch (err) {
if (err instanceof ValidateError) {
for (const detail of err.details) {
console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
}
}
}Safe mode (no try/catch)
安全模式(无需try/catch)
typescript
const validator = ZSchema.create();
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
for (const detail of err.details) {
console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
}
}typescript
const validator = ZSchema.create();
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
for (const detail of err.details) {
console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
}
}Walking nested errors
遍历嵌套错误
Combinators (, , ) produce nested errors. A recursive walker handles any depth:
anyOfoneOfnotinnertypescript
function walkErrors(details: SchemaErrorDetail[], depth = 0): void {
for (const detail of details) {
const indent = ' '.repeat(depth);
console.log(`${indent}[${detail.code}] ${detail.path}: ${detail.message}`);
if (detail.inner) {
walkErrors(detail.inner, depth + 1);
}
}
}
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
walkErrors(err.details);
}组合器(、、)会生成嵌套的错误,递归遍历函数可以处理任意深度的嵌套:
anyOfoneOfnotinnertypescript
function walkErrors(details: SchemaErrorDetail[], depth = 0): void {
for (const detail of details) {
const indent = ' '.repeat(depth);
console.log(`${indent}[${detail.code}] ${detail.path}: ${detail.message}`);
if (detail.inner) {
walkErrors(detail.inner, depth + 1);
}
}
}
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
walkErrors(err.details);
}Collecting all leaf errors
收集所有叶子错误
Flatten the tree to get every concrete error, skipping combinator wrappers:
typescript
function collectLeafErrors(details: SchemaErrorDetail[]): SchemaErrorDetail[] {
const leaves: SchemaErrorDetail[] = [];
for (const detail of details) {
if (detail.inner && detail.inner.length > 0) {
leaves.push(...collectLeafErrors(detail.inner));
} else {
leaves.push(detail);
}
}
return leaves;
}将错误树扁平化,获取所有具体错误,跳过组合器包装层:
typescript
function collectLeafErrors(details: SchemaErrorDetail[]): SchemaErrorDetail[] {
const leaves: SchemaErrorDetail[] = [];
for (const detail of details) {
if (detail.inner && detail.inner.length > 0) {
leaves.push(...collectLeafErrors(detail.inner));
} else {
leaves.push(detail);
}
}
return leaves;
}Mapping errors to form fields
将错误映射到表单字段
Convert JSON Pointer paths to field names for UI form validation:
typescript
function pathToFieldName(path: string | Array<string | number>): string {
if (Array.isArray(path)) {
return path.join('.');
}
// JSON Pointer string: "#/address/city" → "address.city"
return path.replace(/^#\/?/, '').replace(/\//g, '.');
}
function errorsToFieldMap(details: SchemaErrorDetail[]): Record<string, string[]> {
const map: Record<string, string[]> = {};
const leaves = collectLeafErrors(details);
for (const detail of leaves) {
const field = pathToFieldName(detail.path) || '_root';
(map[field] ??= []).push(detail.message);
}
return map;
}
// Usage
const { valid, err } = validator.validateSafe(formData, schema);
if (!valid && err) {
const fieldErrors = errorsToFieldMap(err.details);
// { "email": ["Expected type string but found type number"],
// "age": ["Value 150 is greater than maximum 120"] }
}将JSON Pointer路径转换为字段名,用于UI表单验证:
typescript
function pathToFieldName(path: string | Array<string | number>): string {
if (Array.isArray(path)) {
return path.join('.');
}
// JSON Pointer字符串转换:"#/address/city" → "address.city"
return path.replace(/^#\/?/, '').replace(/\//g, '.');
}
function errorsToFieldMap(details: SchemaErrorDetail[]): Record<string, string[]> {
const map: Record<string, string[]> = {};
const leaves = collectLeafErrors(details);
for (const detail of leaves) {
const field = pathToFieldName(detail.path) || '_root';
(map[field] ??= []).push(detail.message);
}
return map;
}
// 使用示例
const { valid, err } = validator.validateSafe(formData, schema);
if (!valid && err) {
const fieldErrors = errorsToFieldMap(err.details);
// { "email": ["Expected type string but found type number"],
// "age": ["Value 150 is greater than maximum 120"] }
}Using array paths
使用数组格式的路径
Enable for easier programmatic access:
reportPathAsArraytypescript
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] instead of "#/address/city"开启配置,方便编程访问:
reportPathAsArraytypescript
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] 而非 "#/address/city"Filtering errors
过滤错误
Per-call filtering
单次调用过滤
Pass or as the third argument:
includeErrorsexcludeErrorstypescript
// Only report type mismatches
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });
// Suppress string-length errors
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });调用validate时传入第三个参数或:
includeErrorsexcludeErrorstypescript
// 仅上报类型不匹配错误
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });
// 屏蔽字符串长度错误
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });Programmatic post-filtering
编程后置过滤
typescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
const typeErrors = err.details.filter((d) => d.code === 'INVALID_TYPE');
const requiredErrors = err.details.filter((d) => d.code === 'OBJECT_MISSING_REQUIRED_PROPERTY');
}typescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
const typeErrors = err.details.filter((d) => d.code === 'INVALID_TYPE');
const requiredErrors = err.details.filter((d) => d.code === 'OBJECT_MISSING_REQUIRED_PROPERTY');
}Custom error messages
自定义错误消息
Map error codes to user-friendly messages:
typescript
const friendlyMessages: Record<string, (detail: SchemaErrorDetail) => string> = {
INVALID_TYPE: (d) => `${pathToFieldName(d.path)} must be a ${d.params[0]}`,
OBJECT_MISSING_REQUIRED_PROPERTY: (d) => `${d.params[0]} is required`,
MINIMUM: (d) => `${pathToFieldName(d.path)} must be at least ${d.params[1]}`,
MAXIMUM: (d) => `${pathToFieldName(d.path)} must be at most ${d.params[1]}`,
MIN_LENGTH: (d) => `${pathToFieldName(d.path)} must be at least ${d.params[1]} characters`,
MAX_LENGTH: (d) => `${pathToFieldName(d.path)} must be at most ${d.params[1]} characters`,
PATTERN: (d) => `${pathToFieldName(d.path)} has an invalid format`,
ENUM_MISMATCH: (d) => `${pathToFieldName(d.path)} must be one of the allowed values`,
INVALID_FORMAT: (d) => `${pathToFieldName(d.path)} is not a valid ${d.params[0]}`,
};
function toFriendlyMessage(detail: SchemaErrorDetail): string {
const fn = friendlyMessages[detail.code];
return fn ? fn(detail) : detail.message;
}将错误码映射为用户友好的提示消息:
typescript
const friendlyMessages: Record<string, (detail: SchemaErrorDetail) => string> = {
INVALID_TYPE: (d) => `${pathToFieldName(d.path)} 必须是 ${d.params[0]} 类型`,
OBJECT_MISSING_REQUIRED_PROPERTY: (d) => `${d.params[0]} 为必填项`,
MINIMUM: (d) => `${pathToFieldName(d.path)} 最小为 ${d.params[1]}`,
MAXIMUM: (d) => `${pathToFieldName(d.path)} 最大为 ${d.params[1]}`,
MIN_LENGTH: (d) => `${pathToFieldName(d.path)} 长度至少为 ${d.params[1]} 个字符`,
MAX_LENGTH: (d) => `${pathToFieldName(d.path)} 长度最多为 ${d.params[1]} 个字符`,
PATTERN: (d) => `${pathToFieldName(d.path)} 格式非法`,
ENUM_MISMATCH: (d) => `${pathToFieldName(d.path)} 必须是允许的取值之一`,
INVALID_FORMAT: (d) => `${pathToFieldName(d.path)} 不是合法的 ${d.params[0]} 格式`,
};
function toFriendlyMessage(detail: SchemaErrorDetail): string {
const fn = friendlyMessages[detail.code];
return fn ? fn(detail) : detail.message;
}Stopping at first error
遇到第一个错误即停止
For fail-fast scenarios:
typescript
const validator = ZSchema.create({ breakOnFirstError: true });This reports only the first error encountered, reducing noise during iterative fixing.
适用于快速失败的场景:
typescript
const validator = ZSchema.create({ breakOnFirstError: true });这种配置只会返回遇到的第一个错误,减少迭代修复过程中的干扰信息。
Using the keyword field
使用keyword字段
The field tells you which schema keyword triggered the error — useful for categorizing errors programmatically:
keywordtypescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
for (const detail of err.details) {
switch (detail.keyword) {
case 'required':
// handle missing field
break;
case 'type':
// handle type mismatch
break;
case 'format':
// handle format failure
break;
}
}
}keywordtypescript
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
for (const detail of err.details) {
switch (detail.keyword) {
case 'required':
// 处理缺失字段的逻辑
break;
case 'type':
// 处理类型不匹配的逻辑
break;
case 'format':
// 处理格式校验失败的逻辑
break;
}
}
}Reference files
参考文件
For the full error code list with descriptions, see the validating-json-data skill's error-codes reference.
完整的错误码列表及说明,请查看JSON数据验证技能的错误码参考。