☰
express-validator 标准净化器(Sanitizers)完全指南:ValidationChain 内置净化方法详解
2026/10/10 2:19:26 网站建设 项目流程
  • 后端

【免费下载链接】express-validator

An express.js middleware for validator.js.

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

本文聚焦 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): ValidationChain
  • trim():去除字符串两端的空白字符(默认去除空格、制表符、换行等空白);可选传入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(): ValidationChain
  • escape():把字符串中的 HTML 特殊字符(<、>、&、"、')替换为对应的 HTML 实体(&lt;、&gt;、&amp;、&quot;、&#x27;),是抵御 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; }): ValidationChain

normalizeEmail()会把邮箱地址规范化为标准形式,例如把" Foo@Bar.com "规范化为"foo@bar.com"。它支持 11 个可选的布尔选项,针对不同邮件服务商的规范化规则,各选项作用如下:

选项作用
all_lowercase邮箱整体转为小写
gmail_lowercaseGmail 地址转为小写(默认开启)
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_lowercaseOutlook.com 地址转为小写
outlookdotcom_remove_subaddress移除 Outlook.com 的 + 子地址
yahoo_lowercaseYahoo 地址转为小写
yahoo_remove_subaddress移除 Yahoo 的 - 子地址
icloud_lowercaseiCloud 地址转为小写
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.14

toInt(radix?: number)

toInt(radix?: number): ValidationChain

把字符串转换为整数。可选参数radix指定进制(2-36),默认为 10(十进制):

body('age').toInt(); // "42" -> 42 body('hex').toInt(16); // "ff" -> 255

toBoolean、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:

  1. 自定义净化器:直接以(value, meta)调用函数,可返回 Promise(支持异步净化),然后把返回值写回上下文;
  2. 标准净化器:先把字段值(数组则逐元素)转换为字符串,再以(stringifiedValue, ...options)调用对应的 validator.js 函数;
  3. 最后通过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.

项目地址:https://gitcode.com/gh_mirrors/ex/express-validator
点击查看免费下载
上一篇:AzurLaneAutoScript技术架构解析:基于图像识别的碧蓝航线全自动化实现
下一篇:如何为DDE on openEuler开发扩展插件:开发者终极入门指南

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

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

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

立即咨询