如何将含List<IFormFile>的嵌套FromForm数据从JS绑定到模型
问题:ASP.NET Core表单文件列表模型绑定失败
我用JavaScript构建表单并追加数据:
this.historicalPreprintDocs.forEach((round, index) => { form.append(`data[${index}].ppid`, this.selectPPID.PPID) form.append(`data[${index}].round`, round.round) form.append(`data[${index}].preprint`, round.preprint) round.supportingDocumentation ? round.supportingDocumentation.forEach((file, index2) => { form.append(`data[${index}].supportingDocumentation[${index2}]`, file) }) : form.append(`data[${index}].supportingDocumentation`, null) })
但在ASP.NET Core控制器中,模型始终绑定为null,只要把supportingDocumentation设为List<IFormFile>就会触发这个问题。
模型定义如下:
public class HistoricalPreprintReview { [ModelBinder(Name = "ppid")] public int ppid { get; set; } [ModelBinder(Name = "round")] public int round { get; set; } [ModelBinder(Name = "preprint")] public IFormFile preprint { get; set; } [ModelBinder(Name = "supportingDocumentation")] public List<IFormFile> supportingDocumentation { get; set; } }
控制器代码:
[HttpPost] public async Task<ActionResult> UploadHistoricalReview([FromForm] List<HistoricalPreprintReview> data) { // 调试时发现data为null,涉及supportingDocumentation时问题出现 }
能看到请求payload里包含data[0].supportingDocumentation[0]等二进制数据,但无法完成绑定,该怎么解决?
解决方案
1. 移除ModelBinder特性的名称指定
ASP.NET Core的表单模型绑定对集合的命名逻辑很严格,你给每个属性加的[ModelBinder(Name = "...")]会破坏自动绑定。因为绑定列表时,框架需要通过data[index].propertyName的格式匹配属性,而ModelBinder的Name会强制框架寻找单独的ppid字段,而非data[index].ppid。
修改模型,去掉所有ModelBinder特性:
public class HistoricalPreprintReview { public int ppid { get; set; } public int round { get; set; } public IFormFile preprint { get; set; } public List<IFormFile> supportingDocumentation { get; set; } }
2. 删除空值的supportingDocumentation追加逻辑
当没有附件时,你追加的form.append(data[${index}].supportingDocumentation, null)会传递无效空值,干扰列表类型的绑定。直接跳过这个分支即可:
this.historicalPreprintDocs.forEach((round, index) => { form.append(`data[${index}].ppid`, this.selectPPID.PPID) form.append(`data[${index}].round`, round.round) form.append(`data[${index}].preprint`, round.preprint) // 仅存在文件时才追加,无文件时不处理 if (round.supportingDocumentation) { round.supportingDocumentation.forEach((file, index2) => { form.append(`data[${index}].supportingDocumentation[${index2}]`, file) }) } })
3. 确保表单enctype设置正确
提交包含文件的表单必须设置enctype="multipart/form-data",否则文件数据无法正确传输。如果是动态创建表单,要手动设置:
const form = new FormData(); // 若是DOM中的表单元素 document.getElementById('your-form-id').enctype = 'multipart/form-data';
验证绑定逻辑
修改后,控制器的data参数应该能正确接收列表数据。如果还有问题,可以在控制器中打印所有表单键,检查命名是否完全匹配:
[HttpPost] public async Task<ActionResult> UploadHistoricalReview([FromForm] List<HistoricalPreprintReview> data) { // 打印所有表单键,验证命名格式 foreach (var key in Request.Form.Keys) { Console.WriteLine(key); } // 后续业务逻辑 }
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

