- 后端
【免费下载链接】express-validator
An express.js middleware for validator.js.
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.combody().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.
相关推荐
express-validator 全请求体校验:直接验证字符串、数组或数字类型的 req.body
express validator 全请求体校验:直接验证字符串、数组或数字类型的 req.body 本篇技术指南围绕 express validator v6
后端Express-Validator 全请求体验证详解
Express Validator 全请求体验证详解 在 Web 开发中,我们经常需要对 HTTP 请求体进行验证。express validator 作为 E
后端为什么Television比fzf更快?性能优化原理详解
为什么Television比fzf更快?性能优化原理详解 Television是一款跨平台、快速且可扩展的通用模糊查找TUI工具,它在性能上超越了传统工具如fz
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考