☰
express-validator 整 body 校验实战:直接验证字符串、数组与数值型请求体
2026/10/10 1:44:36 网站建设 项目流程
  • 后端

【免费下载链接】express-validator

An express.js middleware for validator.js.

项目地址:https://gitcode.com/gh_mirrors/ex/express-validator
点击查看免费下载

express-validator 的绝大多数校验都围绕req.body中的命名字段展开,但实际业务中经常遇到请求体本身就是字符串(如text/plain的邮箱地址)、数组或数值的场景。本篇以仓库中 整 body 校验文档 为主体,讲解如何通过省略字段路径、直接对req.body整体做验证与清洗,并结合源码实现说明其底层机制。读完本文你将掌握整 body 校验的完整写法、与validationResult、checkExact等 API 的配合方式,以及请求体解析、可选化等边界处理技巧。

适用场景:为什么需要整 body 校验

在 express-validator 中,"字段(field)"通常是请求里的某个具名属性,例如req.body.email、req.query.page。但有时你需要校验的请求体不是对象,而是:

  • 字符串:例如text/plain类型请求中直接携带的邮箱地址、手机号;
  • 数组:例如 JSON 数组形式的批量 ID 列表;
  • 数值:例如直接以数字作为请求体内容的接口。

针对这类请求,express-validator 允许你省略要校验的字段,直接检查整个req.body。这正是官方文档中"Whole Body Validation(整 body 校验)"这一特性的由来。

核心语法:省略字段即选中整个请求位置

check()系列 API 的第一个参数fields用于指定要校验的字段路径。根据 check() API 文档 的签名:

check(fields?: string | string[], message?: any): ValidationChain

当fields被省略时,校验会作用于整个请求位置(whole request location)。也就是说:

body().isEmail(); // 校验整个 req.body body('').isEmail(); // 与上面等价,使用空字符串同样选中整个 req.body

两种写法完全等价,在 字段选择指南 的"Whole-body selection"小节中有明确说明:

app.post( '/recover-password', // These are equivalent. body().isEmail(), body('').isEmail(), (req, res) => { // Handle request }, );

这一特性同样适用于其他请求位置——省略参数调用cookie()、header()、param()、query()会分别选中整个req.cookies、req.headers、req.params、req.query。不过正如官方文档所提示的,在实际业务中整位置校验最常见也最实用的场景仍然是req.body。

完整实战示例:text/plain 找回密码接口

整 body 校验最典型的应用是处理text/plain请求。由于 express 自带中间件主要解析 JSON 和 URL-encoded 表单,纯文本请求体需要借助body-parser的text()中间件来解析:

const bodyParser = require('body-parser'); const express = require('express'); const { body } = require('express-validator'); const app = express(); // Will handle text/plain requests app.use(bodyParser.text()); app.post('/recover-password', body().isEmail(), (req, res) => { // Assume the validity of the request was already checked User.recoverPassword(req.body).then(() => { res.send('Password recovered!'); }); });

这段代码能够正确处理如下请求:

POST /recover-password HTTP/1.1 Host: localhost:3000 Content-Type: text/plain my@email.com

body().isEmail()会把req.body中的字符串("my@email.com")当作整体来校验,通过后进入路由处理器,此时可以安全地把req.body直接交给后续业务逻辑(如User.recoverPassword(req.body))。

版本前提:按当前仓库 docs/index.md 的说明,express-validator 要求应用运行在 Node.js 14+ 及以上,且已验证可配合 express.js 4.x 工作。如果你使用的 express 版本较新(4.16+),也可直接用内置的express.json()等解析中间件,但text/plain场景仍需body-parser的text()或等价方案。

底层原理:从源码看整 body 校验如何工作

整 body 校验的"省略字段"行为并不是文档层面的魔法,而是有明确的源码实现支撑。

1.check()的默认字段就是空字符串

在 check() 实现 中,fields参数的默认值被定义为'':

export function check( fields: string | string[] = '', locations: Location[] = [], message?: FieldMessageFactory | ErrorMessage, ): ValidationChain { const builder = new ContextBuilder() .setFields(Array.isArray(fields) ? fields : [fields]) .setLocations(locations) .setMessage(message); // ... }

因此body()等价于body(''),二者最终都会向ContextBuilder传入['']作为待选择字段。

2.body()只是限定位置的check()

validation-chain-builders.ts 通过buildCheckFunction派生出各个快捷函数:

export const body = buildCheckFunction(['body']);

也就是说body()与check('', ['body'])完全等价,只是把校验范围限定在req.body这一个请求位置。

3. 空路径在字段选择阶段直接取整个位置的值

真正决定"整 body 选中"行为的是 字段选择实现 中的expandField:

const value = path === '' ? req[location] : _.get(req[location], path);

当字段路径为空字符串时,代码不再走_.get()深路径取值,而是直接把req[location](即整个req.body)作为待校验值。这解释了为什么字符串、数组、数值这类"非对象"请求体也能被整体校验——因为校验器拿到的是原始值本身。

4. 测试用例的佐证

字段选择测试 中专门为这一行为写了用例:

it('selects whole location when path is empty', () => { const req = { body: 'shake it, shake it!', }; const instances = selectFields(req, [''], ['body']); expect(instances).toHaveLength(1); expect(instances[0]).toMatchObject({ location: 'body', path: '', originalPath: '', value: 'shake it, shake it!', }); });

该用例印证了两个关键事实:空路径会把整个req.body作为唯一待校验值,且返回的字段实例中path为空字符串——这意味着整 body 校验失败时,错误对象的path字段也会是''。

整 body 校验与周边 API 的配合

校验结果:validationResult()

整 body 校验同样把结果挂载到req上,用 validationResult() 即可判断请求是否合法:

const { body, validationResult } = require('express-validator'); app.post('/recover-password', body().isEmail(), (req, res) => { const result = validationResult(req); if (!result.isEmpty()) { return res.status(400).json({ errors: result.array() }); } // 校验通过,继续处理 });

如果某个验证器未指定自定义错误消息,默认错误消息为Invalid value(见 validation-chain-builders.ts 的注释说明)。

清洗器同样支持整 body

整 body 校验不只限于验证器,清洗器(sanitizer)也可以直接作用于整个请求体。例如:

// 对 text/plain 请求体做 trim 后再处理 app.post('/note', body().trim(), (req, res) => { // req.body 已是 trim 后的字符串 });

这与对象场景中body('title').trim()的用法完全一致,只是目标从具名字段变成了整个req.body。

与checkExact()的配合

如果路由同时使用了 checkExact()(用于拒绝未声明字段的请求),整 body 校验还有一层额外效果:被整体校验的请求位置会被视为"全部已知",checkExact()不会把其中的任何字段标记为未知。

这一点可以从源码推断:在selectUnknownFields中,空路径字段会被转换为['']存入已知字段树(见 field-selection.ts),而findUnknownFields在遇到tree['']时直接认为该位置以下全部被校验覆盖。对应的测试 field-selection.spec.ts 也验证了"整位置已知时不会选出未知字段":

it('selects nothing if whole location is known', () => { const req = { body: 'foobar' }; const instances = selectUnknownFields(req, [''], ['body']); expect(instances).toHaveLength(0); });

手动运行校验链

整 body 校验链同样实现了ContextRunner接口,可以用.run(req)在自定义中间件里手动触发,实现条件校验或统一错误处理。参考 手动运行校验指南 中的validate辅助函数模式:

const validate = validations => { return async (req, res, next) => { for (const validation of validations) { const result = await validation.run(req); if (!result.isEmpty()) { return res.status(400).json({ errors: result.array() }); } } next(); }; }; app.post('/recover-password', validate([body().isEmail()]), handler);

注意事项与边界条件

1. 请求体必须已被解析

整 body 校验的前提是req.body已被中间件解析为实际值。若路由上没有任何 body 解析中间件,req.body通常为undefined,此时body().isEmail()会因值缺失而报错。对策是在应用层挂载对应的解析中间件:

  • text/plain→bodyParser.text();
  • JSON →express.json()(express 4.16+ 内置)或bodyParser.json();
  • URL-encoded →express.urlencoded()或bodyParser.urlencoded()。

2. 使用optional()容忍缺失的请求体

如果某些请求可能根本不携带 body,可以给整 body 校验链加上.optional(),让值为undefined时跳过验证(详见 validation-chain 文档 中关于.optional()的说明):

app.post('/recover-password', body().optional().isEmail(), handler);

3. 数组与数值请求体的校验

由于整 body 校验把值原样交给校验器,数组或数值请求体也可以直接使用链式校验,配合.custom()(见 自定义校验指南)实现更复杂的规则,例如校验数组长度、元素类型等。数值请求体同理,可直接使用isInt、isFloat等数值校验器。

4. 自定义错误消息

既可以在创建校验链时传入统一的兜底消息:

body('', 'The request body must be a valid email').isEmail();

也可以针对单个校验器用.withMessage()覆盖:

body().isEmail().withMessage('Please send a valid email address');

5. 与通配符语法的区别

整 body 校验(省略路径)与 字段选择指南 中的通配符(*)和 globstar(**)是两种不同的机制:前者把整个请求位置作为一个值整体校验;后者则是在对象内部按模式匹配多个子字段,每个匹配项独立校验。对于"请求体本身是字符串/数组/数值"的场景应使用整 body 校验;对于"请求体是对象、但想批量匹配内部字段"的场景才使用*/**。

小结

整 body 校验是 express-validator 处理非对象请求体的标准方案:省略fields参数(或传空字符串)即可让校验与清洗直接作用于整个req.body,底层由 check() 的默认空字段 与 字段选择器对空路径的特殊处理 共同保证,并有 对应单元测试 验证。配合validationResult获取错误、checkExact判定未知字段、.optional()处理缺失请求体,你可以把"请求体即数据"的接口(找回密码、批量操作、纯文本提交等)也纳入到 express-validator 统一的验证与清洗体系中。

相关资源

  • 整 body 校验原始文档
  • 字段选择指南(含整位置选择与通配符)
  • check() / body() API 文档
  • check() 源码实现
  • 字段选择器源码 与 字段选择器测试
  • validationResult 与错误处理
  • checkExact:拒绝未知字段
  • 手动运行校验链
  • 项目快速上手指南
  • 后端

【免费下载链接】express-validator

An express.js middleware for validator.js.

项目地址:https://gitcode.com/gh_mirrors/ex/express-validator
点击查看免费下载
上一篇:Linkedin Scraper实战:5个真实用户资料爬取案例解析
下一篇:GitHub_Trending/vs/vst3sdk中的日志系统:调试和用户支持工具

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询