- 后端
【免费下载链接】express-validator
An express.js middleware for validator.js.
本篇技术指南围绕 express-validator v5.3.0 的Filter API展开,该 API 通过require('express-validator/filter')提供。文章核心覆盖两大能力:用matchedData()从请求中抽取已被checkAPI 校验过的数据并重组为干净对象,以及用sanitize()及其位置变体、buildSanitizeFunction()构建针对不同请求位置的清洗链。阅读完本文,你将掌握每个函数的参数语义、默认值、底层实现原理与实战用法,并能正确将其迁移到 v6/v7 的新式 API。
概述:Filter API 在 express-validator 中的定位
在 express-validator v5.3.0 中,代码按职责拆分为两个独立入口:
require('express-validator/check'):提供check、body、query等校验相关 API;require('express-validator/filter'):提供matchedData、sanitize、sanitizeBody、sanitizeCookie、sanitizeParam、sanitizeQuery、buildSanitizeFunction等数据抽取与清洗相关 API。
Filter API 回答了两个常见问题:"校验通过后,如何方便地拿到一个只含合法数据的干净对象?"以及"如何在不使用整个express-validator中间件的情况下,单独对请求字段做清洗?"本文对应的原版文档位于仓库 website/versioned_docs/version-5.3.0/api-filter.md。
需要注意:在 v6.0.0 之后,express-validator/check与express-validator/filter两个入口均被废弃(会向控制台打印警告),所有 API 统一从express-validator主入口导入,详见 docs/migration-v5-to-v6.md。本文讲解的 API 语义在 v5.3.0 中即为如此,而其底层行为至今仍可在仓库源码src/matched-data.ts与src/express-validator.ts中得到印证。
matchedData(req[, options]):抽取已校验数据
matchedData是 Filter API 的核心函数。它的作用是:从请求中提取已被checkAPI 校验过的数据,并组装成一个对象返回。嵌套路径与通配符(wildcards)都会被正确处理。
函数签名与返回值
matchedData(req[, options])req:Express 的 request 对象。options(可选):一个对象,支持以下选项:includeOptionals:设为true时,返回值包含被标记为 optional 的数据;默认false。onlyValidData:设为false时,返回值将包含未通过校验字段的数据;默认true。locations:一个数组,指定从哪些请求位置抽取数据。可接受的值包括body、cookies、headers、params和query;默认undefined,表示所有位置。
- 返回值:由
checkAPI 校验过的数据组成的对象。
基础示例
原文档给出了一个极具代表性的完整示例,覆盖了按位置抽取与全量抽取两种用法:
// Suppose the request looks like this: // req.query = { from: '2017-01-12' } // req.body = { to: '2017-31-12' } app.post('/room-availability', check(['from', 'to']).isISO8601(), (req, res, next) => { const queryData = matchedData(req, { locations: ['query'] }); const bodyData = matchedData(req, { locations: ['body'] }); const allData = matchedData(req); console.log(queryData); // { from: '2017-01-12' } console.log(bodyData); // { to: '2017-31-12' } console.log(allData); // { from: '2017-01-12', to: '2017-31-12' } });可以看到:不传locations时,matchedData会把不同位置(query、body)的数据合并进同一个对象;传入locations数组后,则只输出指定位置的字段。
三个选项的完整语义
includeOptionals(默认false)当一个字段链被标记为
.optional(),且请求中没有该字段(值为undefined)时,该字段默认不会出现在matchedData的结果中。将includeOptionals设为true后,这些"缺省但可选"的字段也会被纳入结果(值为undefined或按 optional 规则判定)。从源码看,这一选项直接映射到
Context.getData({ requiredOnly })的行为(src/matched-data.ts):当removeOptionals为true时,Context会按字段的optional设置过滤掉undefined、null(optional === 'null'时)或 falsy(optional === 'falsy'时)的值,过滤逻辑见 src/context.ts。onlyValidData(默认true)默认情况下,只要某个字段在对应位置存在校验错误(
error.type === 'field'且location、path匹配),该字段的值就不会被返回。当设为false时,即使字段未通过校验也会被包含进结果。对应源码中的
createValidityFilter(src/matched-data.ts):它为false时直接放行所有数据,为true时则检查该字段在 context 的errors数组中是否存在匹配的 field 类型错误。locations(默认undefined,即全部位置)限定抽取范围。源码中
createLocationFilter(src/matched-data.ts)的实现很直白:locations为空数组时不过滤任何位置;否则只保留locations.includes(field.location)的字段。位置的可选值在 src/base.ts 中定义为'body' | 'cookies' | 'headers' | 'params' | 'query'。
底层工作原理
matchedData的读取路径在 src/matched-data.ts 中非常清晰,可以总结为一条流水线:
- 从
req的express-validator#contexts键(源码常量contextsKey,见 src/base.ts)取出校验中间件在运行时写入的全部Context对象; - 用
flatMap将每个 Context 中的字段实例(FieldInstance)连同其所属 Context 展开成扁平列表; - 用
validityFilter过滤掉有校验错误的字段; - 用
locationFilter过滤掉不在指定位置的字段; - 最后通过 lodash 的
_.set(state, instance.path, instance.value)按字段路径重组出嵌套对象——这正是嵌套路径(如user.name)与通配符(如foo.*)能被正确还原成层级结构的原因。
这一实现还被ExpressValidator类的实例方法matchedData复用(src/express-validator.ts),该方法注释明确说明它是matchedData的快捷方式,行为完全一致。
由测试用例验证的行为细节
仓库中的 src/matched-data.spec.ts 覆盖了文档提到的全部选项组合,可直接作为行为契约:
- 未运行任何校验/清洗链时返回
{}(matchedData({})不会抛错); - 默认情况下只包含有效且非 optional的数据:
check(['foo', 'bar', 'baz']).optional().isInt()作用于{ headers: { foo: 'bla', bar: '123' } }时,结果只有{ bar: '123' }——foo因未通过isInt被剔除,baz因 optional 且缺失被剔除; - 通配符校验结果会被正确合并,如
check(['foo.*', '*.*.qux']).isInt()返回嵌套的{ foo: [1, 2, 3], bar: { baz: { qux: 4 } } }; - 在
oneOf()中失败的链组,其字段即使本身值合法,也不会被包含——避免把"来自失败分支"的数据当作可信数据; includeOptionals: true时,缺失的 optional 字段baz会出现在结果中;onlyValidData: false时,未通过isInt的foo: 'bla'也会被返回;locations: ['params', 'query']时,只返回params与query位置的数据,headers中的数据被忽略。
sanitize(fields):创建通用清洗链
sanitize(fields)fields:一个字段名字符串或字符串数组。- 返回值:一个 Sanitization Chain。
sanitize为单个或多个字段创建清洗链,这些字段可以位于以下任意请求对象中:
req.bodyreq.cookiesreq.paramsreq.query
注意:req.headers在 v5.3.0 中暂不支持。
关键语义:如果某个字段在多个位置同时存在,那么该字段在所有位置的实例都会被清洗。
一个典型的组合用法是"清洗链 + 中间件执行",例如对用户输入做转义与去空格:
const { sanitizeBody } = require('express-validator/filter'); app.post('/contact-us', (req, res) => { sanitizeBody('message').escape().trim(); // ...后续处理 });v5 时代清洗链通常依赖全局中间件或直接执行;迁移到 v6 后,需要改为
await sanitize('message').escape().trim().run(req),并在路由中使用express-validator主入口导入,参见 docs/migration-v5-to-v6.md。
位置限定变体:sanitizeBody/sanitizeCookie/sanitizeParam/sanitizeQuery
四个变体与sanitize(fields)的唯一区别在于清洗位置被锁定:
| 函数 | 作用位置 | 等价写法 |
|---|---|---|
sanitizeBody(fields) | 仅req.body | buildSanitizeFunction(['body'])(fields) |
sanitizeCookie(fields) | 仅req.cookies | buildSanitizeFunction(['cookies'])(fields) |
sanitizeParam(fields) | 仅req.params | buildSanitizeFunction(['params'])(fields) |
sanitizeQuery(fields) | 仅req.query | buildSanitizeFunction(['query'])(fields) |
它们与校验侧body()、cookie()、param()、query()的对应关系一致——在 v5.3.0 的 api-check.md 中,check系列同样按位置拆分。
清洗动作通过 Sanitization Chain 执行,链上的每个清洗器在运行时被封装为Sanitization上下文项(src/context-items/sanitization.ts):数组值会被逐元素清洗,字符串化(toString)后交给 validator.js 的标准清洗器,结果写回 Context 的 dataMap,供后续读取或matchedData使用。
buildSanitizeFunction(locations):自定义位置组合
buildSanitizeFunction(locations)locations:一个请求位置数组,可包含body、cookies、params或query中的任意组合。- 返回值:一个
sanitize()的变体,只清洗传入的这些位置。
buildSanitizeFunction让我们不必为每种组合手写位置判断,而是直接产出绑定了位置集合的清洗函数。原文档示例非常实用:
const { buildSanitizeFunction } = require('express-validator/filter'); const sanitizeBodyAndQuery = buildSanitizeFunction(['body', 'query']); app.put('/update-product', [ // id 无论出现在 req.body 还是 req.query,都会被转换为 int sanitizeBodyAndQuery('id').toInt() ], productUpdateHandler)当业务上允许某个字段"既可能在 body 中,也可能在 query 中"(例如 REST 中 id 可来自路径查询参数)时,buildSanitizeFunction(['body', 'query'])一次即可覆盖两个位置,这正是sanitize(fields)"多位置同时清洗"语义的灵活运用。
常见问题与注意事项
req.headers不被sanitize支持:v5.3.0 的 Filter API 中,清洗侧无法覆盖 headers;如果需要清洗 headers 中的数据,只能在校验侧借助header()与自定义逻辑处理。相比之下,校验侧check系列是支持 headers 的。- 多位置同名字段:字段若同时出现在 body 与 query,
sanitize(fields)会清洗所有实例,这与check中"多位置同时校验"的语义(见 api-check.md)保持一致;底层Context.getData在多位置、多实例场景下还会做去重与"至少包含一个用于报错"的兜底处理(src/context.ts)。 matchedData依赖校验链先运行:matchedData读取的是校验中间件写入req的 contexts 数据(express-validator#contexts)。如果没有任何check链运行过,返回空对象({}),不会抛错——这一点有 src/matched-data.spec.ts 的测试保证。- v6+ 迁移:
express-validator/filter入口在 v6 起废弃,统一改为require('express-validator');同时sanitize系列清洗链需要追加.run(req)才会真正执行,详见 docs/migration-v5-to-v6.md。
小结
Filter API 是 express-validator v5.3.0 中"数据出口"与"数据清洗"的一体化工具:
matchedData(req, options)以三个选项(includeOptionals、onlyValidData、locations)精确控制返回的数据范围,内部借助express-validator#contexts存储、Context.getData过滤与 lodash_.set重组,天然支持嵌套路径与通配符;sanitize(fields)及其四个位置变体(sanitizeBody、sanitizeCookie、sanitizeParam、sanitizeQuery)把清洗链绑定到指定请求位置,buildSanitizeFunction(locations)则进一步支持任意位置组合。
这些行为的正确性在仓库测试 src/matched-data.spec.ts 中有完整覆盖,读者可据此验证本文结论,并在升级到 v6/v7 时无缝迁移 API 形态。
- 后端
【免费下载链接】express-validator
An express.js middleware for validator.js.
相关推荐
把孩子的涂鸦变成会跳舞的AI动画,一条命令搞定
把孩子的涂鸦变成会跳舞的AI动画,一条命令搞定 孩子画的小人永远只是纸上的一团线条,而 AnimatedDrawings 这个开源项目,能让孩子的手绘人物直接动
后端Apache DolphinScheduler 发布组装模块 dolphinscheduler-dist 全解析:二进制包、源码包与 Docker 镜像的构建原理
Apache DolphinScheduler 发布组装模块 dolphinscheduler dist 全解析:二进制包、源码包与 Docker 镜像的构建原
后端express-validator `matchedData()` 完全指南:从请求中提取已验证与已清洗数据
express validator matchedData 完全指南:从请求中提取已验证与已清洗数据 matchedData 是 express validat
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考