☰
express-validator 数据净化(Sanitization)实战指南:用验证链清洗请求字段
2026/10/10 2:16:31 网站建设 项目流程
  • 后端

【免费下载链接】express-validator

An express.js middleware for validator.js.

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

导读

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,签名如下:

方法签名作用
blacklistblacklist(chars: string)删除值中所有属于chars的字符
escapeescape()对<、>、&、"、'做 HTML 转义
unescapeunescape()escape()的逆操作,还原 HTML 实体
ltrimltrim(chars?: string)去除字符串左侧空白(或chars指定字符)
rtrimrtrim(chars?: string)去除字符串右侧空白(或chars指定字符)
trimtrim(chars?: string)去除两侧空白(或chars指定字符)
normalizeEmailnormalizeEmail(options?)规范化邮箱地址(详见下文)
stripLowstripLow(keep_new_lines?: boolean)移除 ASCII 控制字符(默认连换行符也移除)
toBooleantoBoolean(strict?: boolean)把'true'/'1'等转成布尔值;strict 为 true 时只认'true'/'false'
toDatetoDate()把可解析的字符串转成Date对象
toFloattoFloat()把字符串转成浮点数
toInttoInt(radix?: number)按指定进制把字符串转成整数
whitelistwhitelist(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(),其行为可以概括为:

  1. 区分标准与自定义:构造时传入custom标志(true表示自定义净化器)。自定义净化器直接拿到原始值value调用;标准净化器则走字符串转换流程。
  2. 字符串先行转换:因为 validator.js 只处理字符串,标准净化器会先把值stringify再交给 validator.js 函数。非字符串的转换规则在验证链指南中有详细表格:Date对象用toISOString(),null/undefined/NaN转成空字符串,对象用toString(),其余值按原样转字符串。
  3. 数组逐项处理:如果字段值是数组,标准净化器会对每一项分别执行净化;若原值不是数组,会被包装成[value]处理后再取出第一项写回(即保持原有的非数组形态)。
  4. 写回请求:最后通过context.setData(path, newValue, location)把净化后的值写回请求对象的对应位置。

这也解释了为什么净化是"就地修改"的。

重要提醒:净化会变更请求对象

原文档在结尾特别标注了Important:

请注意,净化会修改请求(sanitization mutates the request)。

例如,如果客户端发送的req.body.text是Hello world :>),经过上述链中的trim()与escape()之后,它的值会变成Hello world :&gt;)——首尾空白被去除,>被转义为&gt;。

这一点既是特性也是约束:

  • 好处:后续的验证器看到的是清洗后的值,路由处理器拿到的直接就是干净的、类型正确的数据,无需二次处理;
  • 注意:由于请求被改写,如果你依赖原始输入(例如做审计日志、签名校验),需要在净化之前先拷贝原始值。

链式顺序至关重要

验证链上的方法几乎总是按书写顺序执行,因此净化器放在验证器前还是后会显著影响结果。这一点在验证链指南中有专门讨论:

// 先验证非空、后 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.

项目地址:https://gitcode.com/gh_mirrors/ex/express-validator
点击查看免费下载
上一篇:终极哔哩下载姬完整指南:打造专业级B站视频自动化下载工作站
下一篇:gpui-base 无样式基础层:用 GPUI Kit 构建自有设计系统的行为与基础设施

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

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

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

立即咨询