升级Hapi与Joi版本后FormData文件数组校验报错问题咨询
问题核心原因
升级后报错是两个依赖的默认行为变更共同导致的,和Joi本身的object写法无关:
- Hapi 从v19版本开始(即包名从
hapi改为@hapi/hapi之后),替换了底层multipart表单解析库,默认关闭了同名字段自动合并为数组、自动剥离字段名[]后缀的逻辑。你前端传的字段名是字面量files[],默认配置下解析器不会把多个同名的files[]字段合并成数组,只会保留最后一个上传的文件,且键名就是files[],和你校验规则里写的files键完全不匹配。 - Joi v17 相比v13 收紧了隐式类型转换规则,旧版本会自动尝试将单值/多值字段转换为校验规则声明的数组类型,v17默认不再做这类自动转换,完全严格匹配解析后的payload结构。
修复方案
二选一即可,推荐第二种,命名更规范:
方案1:不修改前端代码,调整后端配置
- 将路由配置里的
multipart: true改为对象配置,通过arrayFields参数声明files[]是数组字段,让解析器自动合并多值 - 调整Joi校验规则的键名,和前端实际传的
files[]字段名完全对应
修正后的后端路由配置代码:
{ method: 'POST', path: '/files', handler: files.save, options: { auth: 'jwt', validate: { // 建议显式包裹Joi.object,避免隐式转换的兼容问题 payload: Joi.object({ title: Joi.string().required(), 'files[]': Joi.array().items(Joi.any()) }) }, payload: { maxBytes: 1048576 * 100, output: 'stream', parse: true, timeout: 60000, multipart: { // 声明数组字段,解析时自动合并同名字段的多个值 arrayFields: ['files[]'] } } } }
方案2:调整前端字段名,后端配置对应匹配
- 前端FormData构造时去掉字段名的
[]后缀,直接使用files作为字段名 - 后端配置
arrayFields: ['files'],原有Joi校验规则的files键无需修改
前端修正代码:
const form = new FormData(); form.append('title', this.state.title); this.state.files?.forEach((item) => { form.append("files", item.file); });
后端对应调整payload配置即可:
payload: { maxBytes: 1048576 * 100, output: 'stream', parse: true, timeout: 60000, multipart: { arrayFields: ['files'] } }
注:之前替换为Joi.object仍报错,是因为没有解决「解析器未合并数组、字段名不匹配」的根问题,和Joi的规则写法无关。
内容的提问来源于stack exchange,提问作者ewein
相关产品推荐
相关产品推荐

