NextJS前端+ASP.NET Core 5 API同时传文件存数据问题
同时上传文件与复杂JSON数据的解决方案(NextJS + ASP.NET Core 5)
问题背景
使用NextJS作为前端、ASP.NET Core 5作为后端Web API,需同时上传文件(对应softwareBillingDocumentFile的PdfFile字段)并提交复杂JSON结构数据,但后端接收时PdfFile为空,无法完成上传。
后端调整
1. 定义统一接收模型
创建包含所有业务数据和文件列表的主模型,替代拆分的参数接收方式:
// 主接收模型,整合所有业务数据与文件列表 public class SoftwareBillingSubmission { // 原JSON结构的所有字段 public string SNo { get; set; } public string ApplicationName { get; set; } public string Status { get; set; } public string ApplicationDescription { get; set; } public string EntryBy { get; set; } public string EntryDate { get; set; } public string Action { get; set; } public List<SoftwareBillingDetail> SoftwareBillingDetails { get; set; } public List<SoftwareBillingDocument> SoftwareBillingDocument { get; set; } public List<SoftwareVendor> SoftwareVendors { get; set; } public List<SoftwareBillingEquipmentInfo> SoftwareBillingEquipmentInfo { get; set; } public string RequestedDate { get; set; } public string ApprovedDate { get; set; } public string ApplicantName { get; set; } public string Designation { get; set; } public string ApplicantEmail { get; set; } public string ApplicantPhone { get; set; } public string ApplicantMobile { get; set; } public string Website { get; set; } public string Remarks { get; set; } public string ApplicationDisName { get; set; } // 文件列表字段 public List<SoftwareBillingDocumentFile> SoftwareBillingDocumentFile { get; set; } } // 补充子模型(匹配原JSON结构) public class SoftwareBillingDetail { public string SNo { get; set; } public string Version { get; set; } public string CertificationNo { get; set; } public string FrontEnd { get; set; } public string BackEnd { get; set; } public string CertificatedDate { get; set; } public string Action { get; set; } } public class SoftwareBillingDocument { public string SNo { get; set; } public string DocumentType { get; set; } public string IncludedYN { get; set; } public string Action { get; set; } } public class SoftwareVendor { public string SNo { get; set; } public string Pan { get; set; } public string VendorName { get; set; } public string VendorType { get; set; } public string ContactNo { get; set; } public string DeveloperForDistibuters { get; set; } public string DeveloperCountry { get; set; } public string Action { get; set; } } public class SoftwareBillingEquipmentInfo { public string SNo { get; set; } public string Version { get; set; } public string EquipSno { get; set; } public string EquipmentName { get; set; } public string BrandName { get; set; } public string ModelNo { get; set; } public string CountryName { get; set; } public string ManufacturingDate { get; set; } public string BuiltInSoftware { get; set; } public string FrontEnd { get; set; } public string BackEnd { get; set; } } // 保留原文件模型不变 public class SoftwareBillingDocumentFile { public string SubmissionNo { get; set; } public object Document { get; set; } public int SeqNo { get; set; } public string DocumentType { get; set; } public string FileName { get; set; } public IFormFile? PdfFile { get; set; } }
2. 修改API接口方法
使用[FromForm]标记主模型,统一接收所有数据:
[HttpPost] public IActionResult SubmitSoftwareBilling([FromForm] SoftwareBillingSubmission submission) { if (submission.SoftwareBillingDocumentFile?.Count > 0) { var fileItem = submission.SoftwareBillingDocumentFile[0]; if (fileItem.PdfFile != null) { var formFile = fileItem.PdfFile; var extension = Path.GetExtension(formFile.FileName).ToLowerInvariant(); if (extension != ".pdf") { return BadRequest("file type not supported"); } var contentLength = formFile.Length / 1024; if (contentLength > 51200) // 限制50MB { return BadRequest("file size greater than 5mb is not allowed"); } // 文件保存逻辑 var fileName = formFile.FileName.Replace(" ", ""); var guidFileName = $"{RandomString(10)}_{fileName}"; var filePath = Path.Combine(Directory.GetCurrentDirectory(), "fileupload", guidFileName); using (var stream = new FileStream(filePath, FileMode.Create)) { formFile.CopyTo(stream); } return Ok("File uploaded and data submitted successfully"); } } return BadRequest("No file or valid data provided"); } // 随机字符串生成方法 private string RandomString(int length) { var random = new Random(); const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; return new string(Enumerable.Repeat(chars, length) .Select(s => s[random.Next(s.Length)]).ToArray()); }
前端调整
1. 状态管理优化
在组件状态中单独存储选中的文件对象,方便后续组装FormData:
import { useState } from 'react'; const YourComponent = () => { // 初始化复杂JSON结构 const [userData, setUserData] = useState({ SNo: "", ApplicationName: "", // ... 其他字段初始化 SoftwareBillingDocumentFile: [] }); const [selectedFile, setSelectedFile] = useState(null); // 文件选择处理 const fileUploadHandler = (e) => { const file = e.target.files[0]; setSelectedFile(file); setUserData(prev => ({ ...prev, SoftwareBillingDocumentFile: [{ FileName: file.name, DocumentType: file.type, SubmissionNo: "800030444804", SeqNo: 0, Document: "" }] })); };
2. 组装FormData并提交
将JSON数据序列化为字符串,与文件一起添加到FormData中,避免嵌套FormData:
// 表单提交方法 const submitForm = async () => { const formData = new FormData(); // 将整个业务数据序列化为JSON字符串 formData.append('SoftwareBillingSubmission', JSON.stringify(userData)); // 添加文件及相关属性,字段名匹配后端模型结构 if (selectedFile && userData.SoftwareBillingDocumentFile.length > 0) { const fileItem = userData.SoftwareBillingDocumentFile[0]; formData.append('SoftwareBillingDocumentFile[0].PdfFile', selectedFile); formData.append('SoftwareBillingDocumentFile[0].FileName', fileItem.FileName); formData.append('SoftwareBillingDocumentFile[0].DocumentType', fileItem.DocumentType); formData.append('SoftwareBillingDocumentFile[0].SubmissionNo', fileItem.SubmissionNo); formData.append('SoftwareBillingDocumentFile[0].SeqNo', fileItem.SeqNo); } try { const response = await fetch('/api/SubmitSoftwareBilling', { method: 'POST', body: formData }); if (response.ok) { const result = await response.json(); console.log('提交成功:', result); } else { const error = await response.text(); console.error('提交失败:', error); } } catch (err) { console.error('请求出错:', err); } }; return ( <div> <input type="file" accept=".pdf" onChange={fileUploadHandler} /> <button onClick={submitForm}>提交</button> </div> ); }; export default YourComponent;
内容的提问来源于stack exchange,提问作者Rajeev Yadav
相关产品推荐
相关产品推荐

