You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Hapi与Joi版本后FormData文件数组校验报错问题咨询

问题核心原因

升级后报错是两个依赖的默认行为变更共同导致的,和Joi本身的object写法无关:

  • Hapi 从v19版本开始(即包名从hapi改为@hapi/hapi之后),替换了底层multipart表单解析库,默认关闭了同名字段自动合并为数组、自动剥离字段名[]后缀的逻辑。你前端传的字段名是字面量files[],默认配置下解析器不会把多个同名的files[]字段合并成数组,只会保留最后一个上传的文件,且键名就是files[],和你校验规则里写的files键完全不匹配。
  • Joi v17 相比v13 收紧了隐式类型转换规则,旧版本会自动尝试将单值/多值字段转换为校验规则声明的数组类型,v17默认不再做这类自动转换,完全严格匹配解析后的payload结构。
修复方案

二选一即可,推荐第二种,命名更规范:

方案1:不修改前端代码,调整后端配置

  1. 将路由配置里的multipart: true改为对象配置,通过arrayFields参数声明files[]是数组字段,让解析器自动合并多值
  2. 调整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:调整前端字段名,后端配置对应匹配

  1. 前端FormData构造时去掉字段名的[]后缀,直接使用files作为字段名
  2. 后端配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:01:46