研发协作
在 next-safe-action…
当需要在服务端动作中返回校验错误(如邮箱已注册)并在客户端按字段或表单展示时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
在 next-safe-action 中特定
解决思路
- 区分两类校验错误来源:schema 校验(输入不匹配 .inputSchema() 时自动触发)和手动校验(在服务端代码中通过 returnValidationErrors() 触发,例如「邮箱已被占用」),两者在客户端产生相同的错误结构。
- 了解默认错误结构(formatted):镜像 schema 结构,每一层都有 _errors 数组,例如根级 _errors 表示表单级错误,email._errors 表示字段错误,address.city._errors 表示嵌套字段错误。
- 使用 returnValidationErrors(schema, errors) 手动返回校验错误:它内部抛出 ActionServerValidationError,由框架捕获并作为 result.validationErrors 返回,且永不返回(总是抛出)。
- 在 schema 校验通过后再检查业务规则,例如先用 db.user.findByEmail(parsedInput.email) 检查邮箱是否已存在,若存在则调用 returnValidationErrors(registerSchema, { email: { _errors: ["This email is already registered"] } })。
- 对多个字段分别做业务校验并分别返回错误,例如再检查用户名是否被占用,若占用则调用 returnValidationErrors(registerSchema, { username: { _errors: ["This username is taken"] } })。
- 所有业务校验通过后再执行实际逻辑,例如 const user = await db.user.create(parsedInput) 并返回 { id: user.id }。
- 使用根级 _errors 返回表单级错误,例如 returnValidationErrors(schema, { _errors: ["You can only create 5 posts per day"] })。
- 对于预期的非校验错误(如「缺货」「未找到」),使用同类辅助函数 returnServerError(payload):它同样内部抛出、永不返回,但会把 result.serverError 设为带类型的 payload,并绕过 handleServerError。
- 在客户端展示 formatted 结构的字段错误:result.validationErrors?.email?._errors?.map((error) => <p key={error} className="text-red-500">{error}</p>)。
- 在客户端展示 formatted 结构的根级错误:result.validationErrors?._errors?.map((error) => <p key={error} className="text-red-500">{error}</p>)。
- 在客户端展示 flattened 结构的字段错误:result.validationErrors?.fieldErrors?.email?.map((error) => <p key={error} className="text-red-500">{error}</p>)。
- 在客户端展示 flattened 结构的表单级错误:result.validationErrors?.formErrors?.map((error) => <p key={error} className="text-red-500">{error}</p>)。
- 参考自定义校验错误与 returnValidationErrors 模式文档(custom-errors.md),以及 formatted 与 flattened 结构、按 action 覆盖的文档(error-shapes.md)。