如何使用Elysia的Typebox实现多校验规则自定义错误提示?
在Elysia中使用TypeBox为不同校验规则设置自定义错误提示
TypeBox本身的error属性仅支持单一错误消息,但可以通过自定义校验规则结合Elysia错误拦截器或手动校验的方式,实现类似Joi的多规则对应不同错误提示的效果。以下是两种可行的实现方案:
方案一:全局错误拦截器(推荐)
通过Elysia的onError钩子统一拦截验证错误,根据错误的code和path映射自定义消息,无需在每个路由重复处理逻辑。
完整代码示例
import { t, type Static, Elysia } from "elysia" // 定义Schema,添加基础校验和自定义Refine规则 export const CreateUserSchema = t.Object({ name: t.String({ minLength: 1 }).Refine((value) => value.trim() !== "", { message: "临时占位", code: "string.empty" }), email: t.String({ format: "email" }).Refine((value) => value.trim() !== "", { message: "临时占位", code: "string.empty" }), password: t.String({ minLength: 6 }).Refine((value) => value.trim() !== "", { message: "临时占位", code: "string.empty" }) }, { // 标记必填字段,触发required错误 required: ["name", "email", "password"] }) export type CreateUserDTO = Static<typeof CreateUserSchema> // 初始化Elysia并配置错误拦截 const app = new Elysia() .onError(({ error, code }) => { if (code === "VALIDATION") { const validationErrors = (error as any).errors return { status: 400, body: { errors: validationErrors.map((err: any) => { // 根据字段路径和错误code返回自定义消息 switch (err.path) { case "/name": if (err.code === "required") return "The name field is required." if (err.code === "minLength") return "The name field is required." if (err.code === "string.empty") return "The name field cannot be empty." break case "/email": if (err.code === "required") return "The email field is required." if (err.code === "format") return "The email field is invalid." if (err.code === "string.empty") return "The email field cannot be empty." break case "/password": if (err.code === "required") return "The password field is required." if (err.code === "minLength") return "The password field must have at least 6 characters." if (err.code === "string.empty") return "The password field cannot be empty." break } return err.message }) } } } }) .post("/users", ({ body }) => body, { body: CreateUserSchema }) .listen(3000)
方案二:手动校验(灵活定制)
使用TypeBox的TypeCompiler预编译Schema,在路由内手动执行校验并返回自定义错误,适合需要针对单个路由定制逻辑的场景。
完整代码示例
import { t, type Static, Elysia } from "elysia" import { TypeCompiler } from "@sinclair/typebox/compiler" export const CreateUserSchema = t.Object({ name: t.String({ minLength: 1 }).Refine((value) => value.trim() !== "", { code: "string.empty" }), email: t.String({ format: "email" }).Refine((value) => value.trim() !== "", { code: "string.empty" }), password: t.String({ minLength: 6 }).Refine((value) => value.trim() !== "", { code: "string.empty" }) }, { required: ["name", "email", "password"] }) export type CreateUserDTO = Static<typeof CreateUserSchema> // 预编译校验器 const CreateUserValidator = TypeCompiler.Compile(CreateUserSchema) const app = new Elysia() .post("/users", ({ body, set }) => { // 执行校验 const isValid = CreateUserValidator.Check(body) if (!isValid) { const errors = CreateUserValidator.Errors(body) const customErrors: string[] = [] for (const err of errors) { switch (err.path) { case "/name": if (err.code === "required") customErrors.push("The name field is required.") if (err.code === "minLength") customErrors.push("The name field is required.") if (err.code === "string.empty") customErrors.push("The name field cannot be empty.") break case "/email": if (err.code === "required") customErrors.push("The email field is required.") if (err.code === "format") customErrors.push("The email field is invalid.") if (err.code === "string.empty") customErrors.push("The email field cannot be empty.") break case "/password": if (err.code === "required") customErrors.push("The password field is required.") if (err.code === "minLength") customErrors.push("The password field must have at least 6 characters.") if (err.code === "string.empty") customErrors.push("The password field cannot be empty.") break } } set.status = 400 return { errors: customErrors } } return body }) .listen(3000)
关键说明
- 必填字段配置:通过
t.Object的required属性标记必填字段,缺失时会触发required错误code。 - 自定义校验规则:使用
t.Refine添加额外校验(如非空判断),并指定自定义code以便后续映射错误消息。 - 错误code对应关系:TypeBox内置校验的code包括:
required: 字段未提供minLength: 字符串长度不足format: 格式不符合(如email)- 自定义
Refine规则可自行指定code
内容的提问来源于stack exchange,提问作者VitorPL
相关产品推荐
相关产品推荐

