返回知识库

数据与服务 · 运行与可靠性

数据校验Validation

在请求入口用 schema 校验输入,错了带字段级错误挡回去——脏数据永远到不了数据库和业务逻辑。

边界校验实验台:在入口挡住脏数据

这是一份注册接口的请求体,已预填坏值。点「校验请求」在入口跑规则,看每个字段挂什么错;切「遇错即停 / 收集全部」对比两种报告;点「不校验·直接写库」看放行后坏值如何在数据库里炸成 500。结果同时长在 字段、响应、数据库 三个物件上。

第 0 步 · 在哪校验:API 入口(路由 / 中间件)用 schema 解析请求体——先解析,再进业务逻辑。前端用同一套规则做即时提示,但服务端永远再验一次,因为客户端可以被绕过。

POST/api/signupreq_7f2
报告方式返回每个字段的错误
响应未发送
// 点「校验请求」在入口解析请求体
数据库 / 业务逻辑

等待入口校验 · 数据库尚未收到请求体。

请求体里是预填的坏值。点「校验请求」在入口跑规则;或点「不校验·直接写库」看放行的后果。

知识点:校验做什么、查什么、返回什么

面板里刚操作过的东西,命名成几条规则。

  • 在边界校验,别信任客户端。请求一进来(路由 / 中间件)就用 schema 解析请求体;前端校验只做体验,服务端永远再验一次,因为请求可以被脚本绕过。
  • 常见四类检查。presence(必填,如未同意条款)、format(邮箱正则)、type(年龄必须是整数)、range / length(年龄 ≥ 13、密码 ≥ 8 位)。
  • 字段级错误,能映射回输入框。返回 { errors: { email: "格式不对" } } 这样的结构,前端把每条错误贴回对应字段——这就是面板里字段挂红的来源。
  • 遇错即停 vs 收集全部。接口里 fail-fast 早返回省力;表单里 collect-all 一次列全所有错,免去用户改一条提交一次。
  • 校验 ≠ 清洗。校验是「不对就拒绝挡回」,清洗(sanitize)是「把可疑输入改干净再用」(如去空格、转义 HTML)。两者目的不同,常一起用,但不能互相替代。

什么时候用

凡是接受外部输入的边界都要校验;纯内部计算不用堆。

API 请求体、表单提交、消息队列消费者、CSV / 上传文件解析、第三方 webhook 回调——只要是「外面进来的数据」,进业务逻辑前都要校验。不适合在已经可信的内部纯函数上重复校验,也不要把校验当成唯一的防御:它是输入约束,权限和幂等性要另外管。

怎么用

从请求到落库的最短路径:定义 schema → 解析 → 失败返 400 → 成功才进业务逻辑。

  1. 用 zod / yup / joi 定义每个字段的类型与约束(必填、格式、范围),前后端共享同一份 schema。
  2. 入口处 parse(requestBody):失败直接返回 400 + 字段级 errors,成功拿到的是已带类型的可信数据。
  3. 业务逻辑只接收解析后的数据;数据库写入前对 invariant(如 start < end、余额 ≥ 0)再做一次断言。
  4. 错误文案说「怎么改」(「邮箱格式不对,应形如 you@site.com」),不要泄漏内部堆栈或表名。

正反例:同一份注册请求,差一个入口校验

只改一个关键选择:要不要在入口解析请求体。

正例 · 入口校验POST /api/signup → 400 { errors: { email: "格式不对" } }

坏值在入口被挡回,用户拿到字段级、可改的反馈;数据库和业务逻辑都没被触碰。

反例 · 不校验直接写库POST /api/signup → 500 InternalError: age_pos constraint

坏值直达数据库,触发约束或让下游业务逻辑抛错;用户看到 500 和内部细节,不知道自己哪填错了。

快速自测

一个活动报名接口收到的请求体是 { start: "2026-09-01", end: "2026-08-30" }。这条该靠哪一类校验挡住、放在哪一步?

继续查证

术语的技术定义和行为以这些一手或权威资料为准。

下一步学

和本知识点经常一起出现的概念。