- 后端
【免费下载链接】express-validator
An express.js middleware for validator.js.
本文聚焦 express-validator 7.x 中ValidationChain提供的全部标准净化器(Standard Sanitizers):从字符串清理(trim、blacklist、whitelist)到类型转换(toBoolean、toDate、toFloat、toInt),再到 HTML 安全(escape/unescape)与邮箱规范化(normalizeEmail)。读完本文,你将掌握每个净化器的函数签名、参数含义与默认行为,理解净化值如何被写回请求对象,并能直接用这些方法构建安全、健壮的 Express 中间件链。
什么是标准净化器
在 express-validator 中,ValidationChain(验证链)拥有三类方法:校验器(validators)、净化器(sanitizers)和修饰器(modifiers)。净化器的职责是变换字段的值——去除噪音、把值转换为正确的 JavaScript 类型,或提供基础的安全防线。与校验器不同,净化器不会产生校验错误,而是把处理后的新值持久化写回请求对象,让后续的 express-validator 函数、你的路由处理器乃至其他中间件都能使用净化后的结果(参见 docs/guides/validation-chain.md 中 "Sanitizers persist the updated field value back into the request" 的说明)。
所谓"标准净化器",指的是 express-validator 从 validator.js 中的Sanitization类实现。
本文所讲解的 14 个标准净化器,均以ValidationChain上方法的形式暴露,完整清单定义于 src/chain/sanitizers.ts 的Sanitizers<Return>接口中,而具体实现则在 src/chain/sanitizers-impl.ts。
字符串清理类净化器
这一类净化器负责去除字符串中的无用字符,是最常用的净化手段,常与校验器组合使用,典型场景如"先trim再isEmail"。
blacklist(chars: string)
blacklist(chars: string): ValidationChain删除字符串中所有出现在chars字符列表里的字符(注意是逐个字符匹配,而非子串匹配)。实现上直接委托给validator.blacklist(src/chain/sanitizers-impl.ts):
// 移除字符串中的所有 'a'、'b' 和 'c' body('code').blacklist('abc'); // 入参 "abc123xyz" -> 净化后 "123xyz"whitelist(chars: string)
whitelist(chars: string): ValidationChain与blacklist相反:只保留chars中列出的字符,其余全部删除。实现同样委托给validator.whitelist(src/chain/sanitizers-impl.ts):
// 只保留数字字符 body('phone').whitelist('0123456789'); // 入参 "(123) 456-7890" -> 净化后 "1234567890"ltrim(chars?: string)/rtrim(chars?: string)/trim(chars?: string)
ltrim(chars?: string): ValidationChain rtrim(chars?: string): ValidationChain trim(chars?: string): ValidationChaintrim():去除字符串两端的空白字符(默认去除空格、制表符、换行等空白);可选传入chars指定要去除的字符集合。ltrim():只去除左端(开头)的空白或指定字符。rtrim():只去除右端(结尾)的空白或指定字符。
三者分别映射到validator.trim/validator.ltrim/validator.rtrim(src/chain/sanitizers-impl.ts):
body('username').trim(); // " john " -> "john" body('title').ltrim('-#'); // "##Hello" -> "Hello" body('trailing').rtrim('!?'); // "Hello!?" -> "Hello"组合顺序的重要性
净化器的执行顺序遵循调用顺序,这点在实际使用中极易踩坑。参考 docs/guides/validation-chain.md 的示例:
// 先校验非空、再 trim —— 可能产生误判 query('search_query').notEmpty().trim();如果用户传入的search_query全是空白字符,notEmpty()会通过,随后trim()把值清空,最终得到"假阳性"。正确的做法是先净化、后校验:
query('search_query').trim().notEmpty();在单元测试 src/chain/sanitizers-impl.spec.ts 中,可以确认ltrim('a')、rtrim('z')、trim('az')等调用都会向上下文添加对应的Sanitization项。
stripLow(keep_new_lines?: boolean)
stripLow(keep_new_lines?: boolean): ValidationChain移除字符串中所有 ASCII 控制字符(0-31 和 127 号字符),用于清理用户输入中的"隐形"控制字符。可选参数keep_new_lines默认为false;设为true时保留换行符(\n),适用于需要在净化后保留多行文本格式的场景:
body('message').stripLow(true); // 删除控制字符但保留换行 body('single_line').stripLow(); // 连换行一并删除escape()/unescape()
escape(): ValidationChain unescape(): ValidationChainescape():把字符串中的 HTML 特殊字符(<、>、&、"、')替换为对应的 HTML 实体(<、>、&、"、'),是抵御 XSS(跨站脚本)攻击的基础手段。unescape():escape()的逆操作,把 HTML 实体还原为普通字符。
实战:用escape()防御 XSS
docs/guides/getting-started.md 中给出了最典型的应用:当用户可以在查询参数中注入<script>标签时,使用escape()将其转义为文本。改造后的路由如下:
const express = require('express'); const { query, validationResult } = require('express-validator'); const app = express(); app.use(express.json()); app.get('/hello', query('person').notEmpty().escape(), (req, res) => { const result = validationResult(req); if (result.isEmpty()) { return res.send(`Hello, ${req.query.person}!`); } res.send({ errors: result.array() }); }); app.listen(3000);访问/hello?person=<b>John</b>时,页面输出 "Hello, <b>John</b>!",原始 HTML 被转义为文本,XSS 注入不再生效。escape()与unescape()在 src/chain/sanitizers-impl.spec.ts 中被验证为以空参数数组([])添加标准净化项。
邮箱规范化:normalizeEmail(options?)
normalizeEmail(options?: { all_lowercase?: boolean; gmail_lowercase?: boolean; gmail_remove_dots?: boolean; gmail_remove_subaddress?: boolean; gmail_convert_googlemaildotcom?: boolean; outlookdotcom_lowercase?: boolean; outlookdotcom_remove_subaddress?: boolean; yahoo_lowercase?: boolean; yahoo_remove_subaddress?: boolean; icloud_lowercase?: boolean; icloud_remove_subaddress?: boolean; }): ValidationChainnormalizeEmail()会把邮箱地址规范化为标准形式,例如把" Foo@Bar.com "规范化为"foo@bar.com"。它支持 11 个可选的布尔选项,针对不同邮件服务商的规范化规则,各选项作用如下:
| 选项 | 作用 |
|---|---|
all_lowercase | 邮箱整体转为小写 |
gmail_lowercase | Gmail 地址转为小写(默认开启) |
gmail_remove_dots | 移除 Gmail 用户名中的点号(如john.doe→johndoe,默认开启) |
gmail_remove_subaddress | 移除 Gmail 的 + 子地址(如john+tag@gmail.com→john@gmail.com,默认开启) |
gmail_convert_googlemaildotcom | 把@googlemail.com转换为@gmail.com(默认开启) |
outlookdotcom_lowercase | Outlook.com 地址转为小写 |
outlookdotcom_remove_subaddress | 移除 Outlook.com 的 + 子地址 |
yahoo_lowercase | Yahoo 地址转为小写 |
yahoo_remove_subaddress | 移除 Yahoo 的 - 子地址 |
icloud_lowercase | iCloud 地址转为小写 |
icloud_remove_subaddress | 移除 iCloud 的 + 子地址 |
需要说明的是,各服务商的选项默认启用情况以 validator.js 的具体实现为准。用法示例:
body('email').normalizeEmail(); // 显式关闭 Gmail 点号去除,保留 "john.doe" 原样 body('email').normalizeEmail({ gmail_remove_dots: false }); // 完全按默认规则规范化后再校验 body('email').normalizeEmail().isEmail();在 src/chain/sanitizers-impl.spec.ts 中可以看到,不传参数调用normalizeEmail()时,options 以undefined传入Sanitization,最终由validator.normalizeEmail使用其内置默认值。值得注意的是,当前仓库最新文档 docs/api/validator/_sanitizers.md 中该签名还额外包含yandex_convert_yandexru?: boolean选项(用于将@yandex.ru转换为@yandex.com),而本指南所对应的 7.0.0 版本文档未包含此项——如果你使用的是更新版本,可查阅上述最新文档确认完整选项集。
类型转换类净化器
这一类净化器把字符串转换为对应的 JavaScript 类型,对于需要从req.body直接拿到数字、日期或布尔值的业务场景非常实用。
toBoolean(strict?: boolean)
toBoolean(strict?: boolean): ValidationChain把字符串转换为布尔值。默认(宽松模式)下,"1"、"true"、"yes"、"on"等会被转换为true,"0"、"false"、"no"、"off"等转换为false,同时保留字符串的大小写不敏感匹配。若传入strict: true,则只接受"true"和"false"(且大小写敏感):
body('newsletter_opt_in').toBoolean(); // "true" -> true, "1" -> true, "false" -> false body('agreed').toBoolean(true); // 仅 "true"/"false" 会被识别,其余保持原值toDate()
toDate(): ValidationChain把日期格式的字符串转换为 JavaScriptDate对象。解析失败时返回NaN(实际上仍会被写回,需配合校验或在使用处判断):
body('birthday').toDate(); // "2016-01-17" -> Date 对象toFloat()
toFloat(): ValidationChain把字符串转换为浮点数(使用parseFloat的解析规则):
body('price').toFloat(); // "3.14" -> 3.14toInt(radix?: number)
toInt(radix?: number): ValidationChain把字符串转换为整数。可选参数radix指定进制(2-36),默认为 10(十进制):
body('age').toInt(); // "42" -> 42 body('hex').toInt(16); // "ff" -> 255toBoolean、toDate、toFloat、toInt的底层分别委托给validator.toBoolean/validator.toDate/validator.toFloat/validator.toInt(src/chain/sanitizers-impl.ts),数组元素的逐项转换同样由Sanitization类负责。
净化器的底层执行原理
理解"净化值如何回到请求对象",有助于排查链式调用中的顺序问题。
标准净化器 vs 自定义净化器
从 src/chain/sanitizers.ts 可以看出,Sanitizers接口包含两类净化方法:
- 自定义净化器:
customSanitizer、default、replace、toArray、toLowerCase、toUpperCase——由 express-validator 自己实现; - 标准净化器:本文讲解的 14 个方法——直接包装 validator.js 的净化函数。
两者的关键差异在 src/chain/sanitizers-impl.ts 中体现:标准净化器通过addStandardSanitization()以custom: false注册Sanitization;而自定义净化器以custom: true注册。
执行流程:Sanitization.run()
核心执行逻辑位于 src/context-items/sanitization.ts:
- 自定义净化器:直接以
(value, meta)调用函数,可返回 Promise(支持异步净化),然后把返回值写回上下文; - 标准净化器:先把字段值(数组则逐元素)转换为字符串,再以
(stringifiedValue, ...options)调用对应的 validator.js 函数; - 最后通过
context.setData(path, newValue, location)把净化后的值写回请求的对应位置(如req.body、req.query、req.params),这正是净化结果能在路由处理器中被读取的原因。
单元测试 src/context-items/sanitization.spec.ts 验证了:标准净化器会逐个处理数组元素(如[1, 42]→ 对每个元素分别调用)、会把附加 options 传给净化函数(如['bar', false])、并持久化净化值回上下文;src/chain/sanitizers-impl.spec.ts 则验证了每个标准净化器方法都正确地以new Sanitization(validatorFn, false, options)注册。
与校验链的组合模式
净化器几乎总是与校验器、修饰器配合使用,形成"净化 → 校验 → 条件控制"的完整链式结构。完整的链式方法参考 docs/api/validation-chain.md,常用组合示例:
const { body } = require('express-validator'); app.post( '/signup', body('email') .trim() // 先净化:去空白 .normalizeEmail() // 再净化:规范化邮箱 .isEmail() // 后校验:必须是合法邮箱 .withMessage('Invalid email'), body('age') .toInt() // 转换为数字 .isInt({ min: 18, max: 120 }), // 再校验年龄范围 body('bio') .optional() .stripLow() // 清除控制字符 .escape(), // 转义 HTML,防 XSS (req, res) => { // req.body.email、req.body.age 已是净化后的值 res.json({ ok: true }); }, );注意:toBoolean、toDate、toFloat、toInt这类转换净化器改变了值的类型,因此放在它们之后的校验器(如isInt)可能不再按字符串规则工作,链式编排时应把类型转换放在校验之前、把纯字符串净化(trim、blacklist等)放在校验之前或之间,依据实际需求决定顺序。
小结
express-validator 的标准净化器把 validator.js 的字符串净化能力无缝接入了 Express 中间件体系,覆盖了日常开发中最常见的三类需求:
- 清理字符串:
trim/ltrim/rtrim(空白与指定字符)、blacklist/whitelist(字符白名单/黑名单)、stripLow(ASCII 控制字符); - 保障安全:
escape/unescape(HTML 实体转义,防 XSS)、normalizeEmail(邮箱规范化,含 Gmail/Outlook/Yahoo/iCloud 等专项规则); - 转换类型:
toBoolean(支持严格模式)、toDate、toFloat、toInt(支持自定义进制)。
每个方法返回ValidationChain自身,因此可以与其他校验器、修饰器无限链式组合。所有标准净化器在底层都会先把值字符串化(数组逐元素处理),再调用 validator.js 函数,最后把结果写回请求对象——理解这一机制,就能在编排净化顺序时避免"先校验后净化"导致的假阳性,写出既安全又符合直觉的校验中间件。
如需查看全部净化器的接口声明,可阅读 src/chain/sanitizers.ts 与 src/chain/sanitizers-impl.ts;完整的验证链 API(含自定义净化器customSanitizer、default、replace、toArray、toLowerCase、toUpperCase等)参见 docs/api/validation-chain.md。
- 后端
【免费下载链接】express-validator
An express.js middleware for validator.js.
相关推荐
express-validator ValidationChain 完全指南:内置校验器、净化器与修饰器精讲
express validator ValidationChain 完全指南:内置校验器、净化器与修饰器精讲 ValidationChain 是 express
后端express-validator 7.2 sanitizer API 完全指南:内置净化器与 ValidationChain 数据清洗实战
express validator 7.2 sanitizer API 完全指南:内置净化器与 ValidationChain 数据清洗实战 导读 本文以 ex
后端express-validator 校验链(ValidationChain)权威指南:内置校验器、净化器与修饰符全解析
express validator 校验链(ValidationChain)权威指南:内置校验器、净化器与修饰符全解析 ValidationChain 是 ex
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考