关于 handling-validation-errors
面向使用 z-schema 处理验证错误的开发者。它说明如何捕获、检查、筛选和转换错误详情,以便在程序逻辑或表单界面中处理。
主要功能
- 01以抛错或安全模式捕获错误
- 02遍历嵌套错误详情
- 03将路径映射到表单字段
- 04按错误代码筛选
- 05自定义错误消息
适用场景
- 01在表单中显示模式错误
- 02处理嵌套的 anyOf 或 oneOf 错误
- 03仅展示选定的验证错误
- 04在首个错误处停止验证
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ zaggino/ z-schema --skill handling-validation-errors安装后在 Claude Code 中输入/handling-validation-errors确认已加载。
详细介绍
处理 z-schema 中的验证错误
z-schema 会将验证错误报告为 ValidateError 对象,其中包含一个 SchemaErrorDetail 数组 .details。本技能介绍如何检查、筛选、映射和呈现这些错误。
错误结构
import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';
ValidateError 继承自 Error:
| 属性 | 类型 | 说明 |
|---|---|---|
.name | string | 始终为 'ValidateError' |
.message | string | 摘要消息 |
.details | SchemaErrorDetail[] | 所有单条错误 |
每个 SchemaErrorDetail:
| 字段 | 类型 | 说明 |
|---|---|---|
message | string | 面向用户的文本,例如 "Expected type string but found type number" |
code | string | 机器可读的代码,例如 "INVALID_TYPE" |
params | (string | number | Array)[] | 用于填充消息模板占位符的值 |
path | string | Array<string | number> | 出错值的 JSON Pointer("#/age" 或 ["age"]) |
keyword | string? | 导致错误的模式关键字("type"、"required" 等) |
inner | SchemaErrorDetail[]? | 组合器(anyOf、oneOf、not)产生的子错误 |
schemaPath | Array<string | number>? | 模式中指向约束的路径 |
schemaId | string? | 存在时的模式 ID |
title | string? | 存在时的模式 title |
description | string? | 存在时的模式 description |
捕获错误
Try/catch(默认模式)
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}`);
}
}
}
安全模式(无需 try/catch)
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}`);
}
}
遍历嵌套错误
组合器(anyOf、oneOf、not)会产生嵌套的 inner 错误。递归遍历器可处理任意深度:
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);
}
收集所有叶级错误
展平树结构以获取每个具体错误,同时跳过组合器包装项:
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;
}
将错误映射到表单字段
将 JSON Pointer 路径转换为字段名称,用于 UI 表单验证:
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"] }
}
使用数组路径
启用 reportPathAsArray,以便更轻松地通过程序访问:
const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] instead of "#/address/city"
筛选错误
按调用筛选
将 includeErrors 或 excludeErrors 作为第三个参数传入:
// Only report type mismatches
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });
// Suppress string-length errors
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });
以编程方式事后筛选
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');
}
自定义错误消息
将错误代码映射为便于用户理解的消息:
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]}`,
ARRAY_UNEVALUATED_ITEMS: () => `Array contains items not covered by any schema`,
OBJECT_UNEVALUATED_PROPERTIES: (d) => `${d.params[0]} is not allowed by the schema`,
COLLECT_EVALUATED_DEPTH_EXCEEDED: (d) => `Schema nesting too deep (max ${d.params[0]})`,
MAX_RECURSION_DEPTH_EXCEEDED: (d) => `Recursion depth exceeded (max ${d.params[0]})`,
};
function toFriendlyMessage(detail: SchemaErrorDetail): string {
const fn = friendlyMessages[detail.code];
return fn ? fn(detail) : detail.message;
}
在首个错误处停止
适用于快速失败场景:
const validator = ZSchema.create({ breakOnFirstError: true });
这只会报告遇到的第一个错误,从而减少迭代修复时的干扰。
使用 keyword 字段
keyword 字段会告知你是哪个模式关键字触发了错误,可用于通过程序对错误进行分类:
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;
}
}
}
参考文件
如需查看完整错误代码列表及其说明,请参阅 validating-json-data 技能的 错误代码参考。
评论
支持 Markdown