解决方案)
后端【免费下载链接】express-validatorAn express.js middleware for validator.js.项目地址https://gitcode.com/gh_mirrors/ex/express-validator点击查看免费下载express-validator 作为 Express.js 生态中最常用的校验中间件其标准校验器/标准清洗器均来自 validator.js而 validator.js 的函数只接受字符串输入。本文基于 v6.10.0 FAQ 文档 的核心问答深入剖析为什么数组不会被正确校验/清洗这一经典陷阱从toString()的类型转换源码逐行拆解到数组只处理首元素的根因再到用通配符*覆盖数组全部元素的实战方案。读完后你将理解 express-validator 的内部值转换链路并能正确写出对数组逐元素生效的校验与清洗规则。一、问题背景标准校验器为何要先做字符串转换在 Validation Chain API 与 Sanitization Chain API 中express-validator 明确说明标准校验器Standard validatorsvalidator.js 暴露的所有校验器如isInt、isEmail、contains、isString等均可直接在链上调用标准清洗器Standard sanitizersvalidator.js 暴露的所有清洗器如normalizeEmail、trim、toInt、escape等。由于validator.js 只接受string作为输入因此任何需要交给标准校验器或标准清洗器处理的值包括数组和对象都必须先被转换成字符串类型。这一转换发生在 express-validator 内部由toString函数完成。也正因如此v6.10.0 的 FAQ 开篇就抛出了那个著名问题为什么数组没有被正确校验/清洗——答案就藏在toString的实现里。二、toString 源码逐行拆解数组为何只处理首元素v6.10.0 FAQ 文档给出了当时版本中toString的完整实现export function toString(value: any, deep true): string { if (Array.isArray(value) value.length deep) { return toString(value[0], false); } else if (value instanceof Date) { return value.toISOString(); } else if (value typeof value object value.toString) { if (typeof value.toString ! function) { return Object.getPrototypeOf(value).toString.call(value); } return value.toString(); } else if (value null || (isNaN(value) !value.length)) { return ; } return String(value); }逐条拆解这段代码可以看到完整的转换优先级分支条件处理逻辑返回示例Array.isArray(value) value.length deep递归调用自身只取数组第一个元素value[0]且传入deep false防止继续递归[sunday, 100]→ 只处理sundayvalue instanceof Date转为 ISO 字符串new Date()→2026-10-09T...Z对象且带有toString优先调用自定义toString()若toString不是函数则回退到原型链上的Object.prototype.toString{ toString: () foo }→foovalue null或isNaN(value) !value.length返回空字符串null/undefined/NaN→其余情况兜底使用原生String(value)false→false100→100关键结论当校验或清洗的目标是数组时只有数组的第一个元素会被送入 validator.js 的校验器/清洗器其余元素被完全忽略。FAQ 原文对此的表述是As we can see above, when validating or sanitizing anarrayonly the first element of it is processed.从仓库源码看utils.spec.ts 中的测试用例也印证了转换规则false → false、null → 、undefined → 、NaN → 、Date 对象 → ISO 字符串、带自定义toString的对象 → 自定义返回值、普通对象 →[object Object]、数组[1, 2, 3]→1,2,3数组整体被String()化为逗号拼接的字符串。这些用例为理解转换边界提供了可验证的依据。版本演进提示需要说明的是上述带数组分支的toString是 v6.10.0 文档记录的实现。在当前仓库源码 src/utils.ts 中toString已经去掉了数组递归分支数组的逐元素处理被下沉到了具体的校验/清洗 context item 中详见第五节。FAQ 所描述的问题形态主要针对 v6.10.0 及更早版本但值会先被字符串化再交给 validator.js这一核心约束在后续版本中依然成立。三、问题复现isString 校验数组为何会误判通过FAQ 给出了一个非常典型、也非常容易踩坑的例子// weekdays: [sunday, 100] body(weekdays).isString(); // 通过校验错误地 body(weekdays.*).isString(); // 不通过校验正确地两个链条的差异解释如下第一条链body(weekdays).isString()字段名直接指向整个数组。由于只处理首元素实际被校验的值是sunday字符串isString(sunday)返回true校验错误地通过了——即使数组里混入了数字100也毫不知情。第二条链body(weekdays.*).isString()字段名使用通配符逐个命中数组的每个元素。元素sunday通过而元素100数字不满足isString校验正确地返回错误。FAQ 原文对此的总结是In this example the first chain processes only the first element of the array and the validation erroneously passes. In the second one, instead, all the elements are validated and the chain correctly returns an error.需要注意的是只处理首元素影响的不仅是校验器清洗器同样受影响。例如body(weekdays).trim()只会对首元素做trim数组里的其他元素会原样保留想要逐元素清洗同样需要通配符。四、解决方案用通配符 * 校验/清洗数组的全部值FAQ 给出的官方建议非常明确You can use wildcards to validate/sanitize all the values of the array.通配符wildcard用*字符表示适用于对数组的所有元素或对象的所有键应用相同规则的场景。以 v6.10.0 的通配符文档 为例假设你要校验所有地址的邮政编码合法并把每个地址的number字段清洗为整数const express require(express); const { check } require(express-validator); const app express(); app.use(express.json()); app.post( /addresses, check(addresses.*.postalCode).isPostalCode(), check(addresses.*.number).toInt(), (req, res) { // Handle the request }, );这段代码可以同时正确处理两种请求体形态形态一地址是数组{ addresses: [ { postalCode: 2010, number: 500 }, { postalCode: , number: 501 } ] }addresses.*.postalCode会命中数组中的每个元素2010与第二个地址的邮政编码为空isPostalCode校验失败——这正是通配符的价值没有通配符时只有数组的第一个地址会被校验。形态二地址是预定义键的对象{ addresses: { home: { postalCode: , number: 501 }, work: { postalCode: 2010, number: 500 } } }*同样可以匹配对象的所有键home、workhome.postalCode为空导致校验失败。通配符对数组元素和对象键是通用的。4.1 通配符与数组相关 API 的配合使用在 v6.10.0 的 Validation Chain API 中还有两处与数组强相关的细节值得注意.notEmpty()同样只校验首元素文档明确指出这不是用来检查数组长度大于零的因为.notEmpty()只会校验数组的第一个元素。因此// weekdays: [sunday, monday] check(weekdays).notEmpty(); // 通过校验 // names: [, John] check(names).notEmpty(); // 不通过因为 names[0] 为空如果需要数组长度至少为 1请改用.isArray({ min: 1 })。.isArray(options)接受{ min, max }选项分别限定数组的最小、最大长度这是对数组整体做校验的正确方式与通配符的逐元素校验是互补关系。五、源码纵深当前版本中数组是如何逐元素处理的虽然 FAQ 成文于 v6.10.0但当前仓库的源码已经对数组处理做了重构从只取首元素演进为逐元素处理。从源码结构看这一变化体现在两个 context item 中1. 标准校验器src/context-items/standard-validation.tsasync run(context: Context, value: any, meta: Meta) { const values Array.isArray(value) ? value : [value]; values.forEach(value { const result this.validator(this.stringify(value), ...this.options); if (this.negated ? result : !result) { context.addError({ type: field, message: this.message, value, meta }); } }); }当前实现会把数组展开成多个值对每个元素分别调用stringify即toString后再交给 validator.js 校验器并针对每个失败元素单独记录错误。这一点在 standard-validation.spec.ts 的测试中得到了印证对数组[1, 42]toString会被调用两次validator 分别收到hey1与hey42。2. 标准清洗器src/context-items/sanitization.tsconst values Array.isArray(value) ? value : [value]; const newValues values.map(value { return (this.sanitizer as StandardSanitizer)(this.stringify(value), ...this.options); }); // We get only the first value of the array if the original value was wrapped. context.setData(path, values ! value ? newValues[0] : newValues, location);清洗器同样对数组逐元素执行清洗并把清洗后的数组整体写回字段。由此可以推断在新版本中body(weekdays).isString()这类直接作用于整个数组的校验其行为与 v6.10.0 已有本质区别——数组的每个元素都会被逐一校验。但仍有两点约束始终存在字符串化仍发生在 validator.js 之前无论数组还是单个值最终进入 validator.js 的都是字符串。例如数字100会被转为100再交给校验器这会带来类型上的失真设计校验规则时需时刻留意。通配符仍然是最明确、最可控的逐元素方案它让你显式声明我要校验数组的每一项而非依赖数组展开的隐式行为同时它对对象键同样生效语义更清晰。六、实战建议与常见误区总结把 FAQ 的结论落到工程实践可以归纳为四条准则校验/清洗数组元素优先使用通配符body(items.*).isInt()、body(tags.*).trim()。通配符是 v6.10.0 FAQ 给出的官方推荐方案也是语义最清晰的做法。对数组整体做约束使用专门的 API数组长度用.isArray({ min, max })存在性检查用.exists()/.optional()不要指望notEmpty()或普通标准校验器替你判断数组结构。留意类型转换失真任何标准校验器/清洗器拿到手的一定是字符串。数字、布尔、对象在被校验前都会先经过toString的转换规则参考 utils.spec.ts 中的用例必要时用.custom()校验原始值类型。结合字段选择规则设计复杂路径通配符可与嵌套路径组合如addresses.*.postalCode覆盖数组与对象两种数据形态更完整的字段选择能力可参考 字段选择指南。最后补充 FAQ 中记录的三个关联问题编号#791、#883、#931它们分别对应了数组校验/清洗行为相关的历史缺陷与讨论可作为追溯该问题演进脉络的线索。七、小结数组没有被正确校验/清洗并非 express-validator 的随机缺陷而是其设计约束下的必然结果validator.js 只认字符串而 v6.10.0 的toString转换函数只取了数组首元素。理解这条转换链路src/utils.ts 中的toString及 utils.spec.ts 的测试用例再配合通配符*对数组逐元素命中就能完全避开这个陷阱。对于使用新版仓库代码的读者标准校验器/清洗器已改为逐元素处理但先字符串化再校验的约束与通配符是最显式方案的结论依然成立两者结合使用才能写出既正确又易读的数组校验规则。赞分享后端【免费下载链接】express-validatorAn express.js middleware for validator.js.项目地址https://gitcode.com/gh_mirrors/ex/express-validator点击查看免费下载相关推荐RevokeMsgPatcher微信QQ TIM防撤回多开工具RevokeMsgPatcher微信QQ TIM防撤回多开工具 RevokeMsgPatcher 是一款 Windows 平台的离线工具为 PC 版微信、Q后端express-validator 中数组为何只校验首元素从 toString 转换到通配符 * 的完整解析express validator 中数组为何只校验首元素从 toString 转换到通配符 的完整解析 本文围绕 express validator v6.后端express-validator 通配符Wildcards与 Globstars 深度指南一次校验数组与对象的所有字段express validator 通配符Wildcards与 Globstars 深度指南一次校验数组与对象的所有字段 本篇技术指南聚焦 express后端上一篇ParlAI Model Chat 众包任务指南采集人类与模型对话数据并批量评估生成质量下一篇弹幕为什么会丝滑bullet-screen-cj消息循环与双线程绘制架构源码剖析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考