- 后端
【免费下载链接】express-validator
An express.js middleware for validator.js.
导读
HTTP 请求中的输入往往不只存在"格式对不对"的问题,还夹杂着前后空白、HTML 标签、大小写不一、多余字符等"噪音"。express-validator 依托 validator.js 提供了一整套内置净化器(sanitizer),并允许通过customSanitizer()编写自定义净化逻辑,在验证的同时就地清洗并改写请求数据。读完本文,你将掌握在验证链中串联normalizeEmail、trim、escape、toBoolean等净化器的完整用法、每种内置净化器的参数与行为、净化器的底层执行原理,以及"净化会变更请求"这一关键注意事项。
什么是数据净化(Sanitization)
在 version-6.7.0 的 Sanitization 特性文档 中,express-validator 对净化给出的定义非常直白:
接收 HTTP 请求中的输入,不只是要确保数据格式正确,还要确保它不含噪音。
验证(validation)负责判断数据是否符合预期格式;而净化(sanitization)负责变换字段的值:去掉噪音、把值转换成正确的 JavaScript 类型,甚至提供一层基础的安全防线。两者的定位可以从验证链指南中看出——验证链的方法分为三类:验证器(validators)、净化器(sanitizers)和修饰器(modifiers)。净化器会把更新后的字段值写回请求对象,这样下游的 express-validator 函数、你自己的路由处理器,乃至其他中间件都能直接使用清洗后的数据。
在验证链中使用内置净化器
净化器和验证器一样,都是验证链上的方法,可以随意串联。原文档给出的/comment示例完整展示了"验证 + 净化"混用的典型写法:
const express = require('express'); const { body } = require('express-validator'); const app = express(); app.use(express.json()); app.post('/comment', [ body('email') .isEmail() .normalizeEmail(), body('text') .not().isEmpty() .trim() .escape(), body('notifyOnReply').toBoolean() ], (req, res) => { // Handle the request somehow });解读这条链:
email字段先被isEmail()验证,再被normalizeEmail()规范化——例如把"FOO@BAR.com"统一成"foo@bar.com";text字段先被not().isEmpty()验证非空,再由trim()去掉首尾空白、escape()将 HTML 特殊字符(<、>、&、"、')转义为实体;notifyOnReply字段完全没有被验证,但它依然借用了同一个body()检查函数,通过toBoolean()把'true'、'1'等值转换成 JavaScript 布尔值。
值得强调的是第三点:净化器不要求字段先通过任何验证,一个字段即使只做净化、不做验证,也完全可以——这正是原文档特意指出的用法:"notifyOnReply字段没有被验证,但它仍然可以利用同一个check函数将其转换为 JavaScript 布尔值"。
内置净化器速查:来自 validator.js 的标准净化器
express-validator 中大部分净化功能来自 validator.js 的Sanitizers接口中,官方 API 文档见 docs/api/validator/_sanitizers.md,签名如下:
| 方法 | 签名 | 作用 |
|---|---|---|
blacklist | blacklist(chars: string) | 删除值中所有属于chars的字符 |
escape | escape() | 对<、>、&、"、'做 HTML 转义 |
unescape | unescape() | escape()的逆操作,还原 HTML 实体 |
ltrim | ltrim(chars?: string) | 去除字符串左侧空白(或chars指定字符) |
rtrim | rtrim(chars?: string) | 去除字符串右侧空白(或chars指定字符) |
trim | trim(chars?: string) | 去除两侧空白(或chars指定字符) |
normalizeEmail | normalizeEmail(options?) | 规范化邮箱地址(详见下文) |
stripLow | stripLow(keep_new_lines?: boolean) | 移除 ASCII 控制字符(默认连换行符也移除) |
toBoolean | toBoolean(strict?: boolean) | 把'true'/'1'等转成布尔值;strict 为 true 时只认'true'/'false' |
toDate | toDate() | 把可解析的字符串转成Date对象 |
toFloat | toFloat() | 把字符串转成浮点数 |
toInt | toInt(radix?: number) | 按指定进制把字符串转成整数 |
whitelist | whitelist(chars: string) | 只保留值中属于chars的字符 |
其中normalizeEmail的完整选项(来自 docs/api/validator/_sanitizers.md)包括:
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; yandex_convert_yandexru?: boolean; }): ValidationChain这些选项默认均为true,用于决定是否对 Gmail、Outlook、Yahoo、iCloud、Yandex 等主流邮箱服务商的地址做小写化、去点、去子地址(+tag)等规范化处理。
由 express-validator 自身实现的净化器
并非所有净化器都来自 validator.js。在 src/chain/sanitizers-impl.ts 中可以看到,default、replace、toArray、toLowerCase、toUpperCase五个方法是通过customSanitizer()内部实现的:
default(default_value):当字段值是undefined、null、NaN或''之一时,替换为default_value;注意实现里使用_.cloneDeep(default_value),因此每次请求拿到的默认对象都是独立克隆,不会互相污染(对应测试见 sanitizers-impl.spec.ts)。replace(values_to_replace, new_value):字段值等于给定值(可传单个值或数组)时替换为new_value,同样会对替换值做深拷贝。toArray():把值包装成数组;若值本身是数组则原样保留,undefined转为[],字符串'foo'转为['foo']。toLowerCase()/toUpperCase():仅当值是字符串时才做大小写转换,非字符串原样返回。
而customSanitizer(sanitizer)本身则用于接入任意自定义净化逻辑。
自定义净化器:customSanitizer()
当内置净化器无法满足需求时,可以用customSanitizer()编写自定义净化器。它在 docs/api/validation-chain.md 中的签名是:
customSanitizer(sanitizer: (value, { req, location, path, pathValues }) => any): ValidationChain自定义净化器几乎没有规则限制:你返回什么值,字段就变成什么值。它同样支持异步——如果返回 Promise,会被 await,最终 resolve 的值写回字段。这一点在 docs/guides/customizing.md 中有完整的示例,例如把路径参数id从字符串转换成 MongoDB 的ObjectId:
import { param } from 'express-validator'; import { ObjectId } from 'mongodb'; app.post( '/user/:id', param('id').customSanitizer(value => ObjectId(value)), (req, res) => { // req.params.id is an ObjectId now }, );自定义净化器的回调会收到(value, meta),其中meta包含req、location、path、pathValues等字段信息(类型定义见 src/base.ts 的Meta),因此你可以根据请求上下文决定如何变换值。
注意事项:如果你在自定义净化器中忘记
return,字段会变成undefined!
底层原理:净化器是如何执行的
从源码层面看,每个净化器调用最终都会创建一个Sanitization上下文项,追加到ContextBuilder的栈中(addItem,见 src/context-builder.ts)。真正执行净化的是 src/context-items/sanitization.ts 中的Sanitization.run(),其行为可以概括为:
- 区分标准与自定义:构造时传入
custom标志(true表示自定义净化器)。自定义净化器直接拿到原始值value调用;标准净化器则走字符串转换流程。 - 字符串先行转换:因为 validator.js 只处理字符串,标准净化器会先把值
stringify再交给 validator.js 函数。非字符串的转换规则在验证链指南中有详细表格:Date对象用toISOString(),null/undefined/NaN转成空字符串,对象用toString(),其余值按原样转字符串。 - 数组逐项处理:如果字段值是数组,标准净化器会对每一项分别执行净化;若原值不是数组,会被包装成
[value]处理后再取出第一项写回(即保持原有的非数组形态)。 - 写回请求:最后通过
context.setData(path, newValue, location)把净化后的值写回请求对象的对应位置。
这也解释了为什么净化是"就地修改"的。
重要提醒:净化会变更请求对象
原文档在结尾特别标注了Important:
请注意,净化会修改请求(sanitization mutates the request)。
例如,如果客户端发送的req.body.text是Hello world :>),经过上述链中的trim()与escape()之后,它的值会变成Hello world :>)——首尾空白被去除,>被转义为>。
这一点既是特性也是约束:
- 好处:后续的验证器看到的是清洗后的值,路由处理器拿到的直接就是干净的、类型正确的数据,无需二次处理;
- 注意:由于请求被改写,如果你依赖原始输入(例如做审计日志、签名校验),需要在净化之前先拷贝原始值。
链式顺序至关重要
验证链上的方法几乎总是按书写顺序执行,因此净化器放在验证器前还是后会显著影响结果。这一点在验证链指南中有专门讨论:
// 先验证非空、后 trim:如果用户传的是纯空白,验证会通过,但 trim 后字段变成空——造成误判 query('search_query').notEmpty().trim();正确的做法是把净化器放在验证器之前,让验证器检查的是清洗后的值:
// 先 trim、后验证非空:纯空白会被去掉,notEmpty() 才能给出正确结论 query('search_query').trim().notEmpty();进阶:通过ExpressValidator复用自定义净化器
如果多个路由都要用到同一个自定义净化器,可以在实例化ExpressValidator时注册,让自定义净化器像内置方法一样可用。示例来自 docs/guides/customizing.md:
import { ExpressValidator } from 'express-validator'; const { body, param, validationResult } = new ExpressValidator( { isPostID: async value => { // Verify if the value matches the post ID format }, }, { muteOffensiveWords: value => { // Replace offensive words with *** }, }, ); app.post( '/forum/:post/comment', param('post').isPostID(), body('comment').muteOffensiveWords(), (req, res) => { const result = validationResult(req); // Handle new post validation result }, );ExpressValidator构造函数的第二个参数即自定义净化器集合,注册后即可像body('comment').muteOffensiveWords()一样链式调用。此外,在checkSchema的字段定义中,标准与自定义净化器同样可以声明使用(详见 schema-validation 指南)。
总结
数据净化是 express-validator 验证链上与验证并行的另一大能力:通过trim、escape、normalizeEmail、toBoolean等内置净化器消除噪音、转换类型,通过customSanitizer实现任意自定义变换,并通过default、replace等便捷方法处理缺失值与替换逻辑。使用时记住三条原则:净化器会就地改写请求数据、净化器与验证器的书写顺序决定行为、标准净化器会把非字符串值先转成字符串再处理。掌握这些要点,你就能写出既验证严格又输出干净数据的 Express 接口。
- 后端
【免费下载链接】express-validator
An express.js middleware for validator.js.
相关推荐
express-validator 数据净化(Sanitization)实战指南:清洗 HTTP 请求输入的完整方案
express validator 数据净化(Sanitization)实战指南:清洗 HTTP 请求输入的完整方案 处理 HTTP 请求输入,很多时候不止要确
后端MindSpeed-LLM推理部署指南:如何高效运行Qwen3-0.6B模型进行文本生成
MindSpeed LLM推理部署指南:如何高效运行Qwen3 0.6B模型进行文本生成 MindSpeed LLM是昇腾AI生态的重要技术支撑,专为大规模语言
express-validator 数据净化(Sanitization)指南:让 HTTP 请求数据既合法又无噪声
express validator 数据净化(Sanitization)指南:让 HTTP 请求数据既合法又无噪声 在 Express 应用中处理 HTTP 请
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考