Node.js 参数校验:前端可信、后端要有底线

2026-09-28 81 浏览 0 评论 0 字

前端一定会做校验,但后端不能只靠前端。原因很简单:你根本不知道请求是从哪个客户端、哪个脚本、哪个 仿冒接口 发出来的。这篇讲参数校验在真实开发里到底该如何写 - 校验什么、怎么组织、怎么防止过度校验把自己写死,以及校验和 业务状态判断 之间的边界。重点:校验不是 都有就行 ,而是 在正确的位置、用正确的力度,拦住不该进的东西 。

一、为什么 前端已经校验了 还不够

前端校验(比如必填、格式、长度)解决的是 用户体验 :帮用户尽早发现输错,少发一次请求。但它挡不住:

  1. 绕过前端直接调接口 的工具(Postman、脚本、爬虫)。
  2. 传进来的数据是 合法类型但不合法值 (比如填了超大整数、超长字符串)。
  3. 恶意构造的注入载荷 (SQL 注入、XSS payload)。

所以后端校验的本质是: 不信任任何输入 。它不是重复前端的活儿,而是给 入口 焊死一道门。

二、这四类输入属性是校验重点

输入属性要拦什么例子
必填/是否缺失null / undefined / 空串创建必须带 name
类型正确类型不符(字符串传成对象、整数传成浮点)id 必须是数字
范围/长度越界值年龄 0-150、字符串超过 255
合法性/枚举值不属于允许集合status 只能是 0/1、type 只能是 a/b/c

其中 枚举值 尤其容易漏 - 很多人只查了 在不在 ,没查 在不在允许的集合里 。这会导致脏数据被写进库里,后面全乱。

三、统一校验 vs 散落校验

校验不该散落在每个接口里各写各的,最好有一个统一入口。常见的两层做法:

方式一:声明式 schema 校验(框架提供的校验器)

很多框架(Egg 的 egg-validate )支持在 Controller/路由层声明规则:

const rule = {
  pageNum: { type: 'int', min: 1, required: false },
  pageSize: { type: 'int', min: 1, max: 1000, required: false },
  username: { type: 'string', max: 50 },
};
this.ctx.validate(rule, this.ctx.query);   // 不符合就抛出一处统一的 400

优点:规则一次声明、错误统一、可读性好。缺点:规则长了之后容易 实在稠密 。

方式二:在 Service 里做必要守卫

对于 业务级别 的合法性(典型: id 对应记录存不存在、状态是否允许这个操作),放在 Service 里更合理,因为它需要查库、依赖业务状态:

async update(data) {
  const rec = await this.model.User.findByPk(data.id);
  if (!rec) return { code: 404, msg: '记录不存在' };   // 业务校验在 service
  // ...校验状态是否允许修改,再改
}

明确分工的推荐做法:

  • 格式/类型/必填/枚举 → 声明式 schema 校验 (请求入口统一拦);
  • 是否存在、状态是否允许、是否越权 → Service 里的守卫 (依赖上下文)。

把这两层分开,既不会让校验器过度膨胀,也不会让 Service 赔上大量重复的格式判断。

四、校验常见的两个误区

误区一:过度校验,把 需求合法 也拦了

有时开发者图省事,把所有字段都设 required: true ,结果漏传一个可选字段,接口就 400,前端却全厂抓瞎。关键: 只校验 真的必须的 ,可选字段回填空跑。 后面要了面面满分,前端体验逆向崩。

误区二:把 业务判断 当 参数校验 混在一个洞里

编程里 这个状态不能改 这个不能删自己 属于 业务规则 ,不是参数格式。把它们混进 schema 校验会变得又丑又难维护。正确做法是 格式校验走 schema,规则校验走 service 。

五、这几个用例特别提防

  1. id 用字符串、或超大溢出的整数 :比如把 id 填成 9999999999999 这种远超 int 范围的值。校验应包含 是数字 是正整数 。
  2. 字符串上限 :如字段最长 255,超长会导致 DB 截断/报错或注入面扩大。必填限长。
  3. body 和 query 混回传 :分清并分别校验;很多人漏了 body 里嵌套的值。
  4. 枚举 :不给状态码一个合法集合,随业务逻辑乱来。

六、校验失败怎么返回

配合我们前面讲的全局异常,最舒服的写法是 校验失败抛一个生一个统一的业务异常 ,统一出口返回:

{ "code": 400, "message": "参数错误", "success": false }

或把具体是哪个字段错了拼进 message (前端好提示)。但 注意别把校验异常和系统异常搅一起 - 校验失败是 业务拒绝 ,堆栈没必要给前端,message 给一句人话即可。

七、小结

参数校验是后端 底线 的具现:

  • 前端校验是 体验 ,后端校验是 底线 ,两个都要有;
  • 按 类型/格式/必填 枚举/范围 这些高优先级拦,别漏 枚举 ;
  • 格式校验走 schema,业务规则走 service ,别混;
  • 别过度校验误伤合法请求,可选字段别强置 required;
  • 校验失败走统一业务异常,错误信息人话、不给堆栈。

下一篇我们把 参数校验 和 鉴权 连起来。虽然它们都守卫入口,但干的事不一样 - 鉴权中间件管 你是不是那个能进来的人 ,白名单又怎么优雅地放行 不需要登录就能访问 的接口。


发布评论

发布评论前请先 登录。
0 评论
点赞
收藏

评论列表 0

暂无评论