ASP.NET MVC中如何通过jQuery AJAX传多模型同时上传文件
问题根因
- 向FormData追加JSON序列化后的复杂对象字符串时,ASP.NET MVC默认模型绑定器不会自动将字符串反序列化为
HealthCard、EmployeeEmploymentHistory类型实例,导致复杂属性绑定失败 - 直接传递普通JS对象发起请求时,jQuery默认序列化逻辑无法解析二进制文件字段,导致
IFormFile类型的附件属性无法被后端接收 - 原有代码构造了FormData实例但实际传入ajax的是普通JS对象,FormData未生效;且ajax配置中的
enctype属性仅对原生表单提交有效,对ajax请求无作用。
可行解决方案
前端代码修改
核心逻辑是按照ASP.NET MVC复杂类型绑定的命名规则,逐字段向FormData追加属性值,复杂子对象的字段统一加属性名.前缀,同时正确传入文件对象,最终把构造完成的FormData传入ajax即可,原有两个构造JS对象的方法无需修改。
function SaveDocumentData() { $('#spinneraj').show(); var formData = new FormData(); // 追加附件文件字段 var attachmentInput = $('#AttachmentFile')[0]; if (attachmentInput.files.length > 0) { formData.append("attachmentFile", attachmentInput.files[0]); } // 逐字段追加healthCard子对象属性,统一加healthCard.前缀匹配绑定规则 var healthCard = getDocumentObject(); Object.keys(healthCard).forEach(key => { formData.append(`healthCard.${key}`, healthCard[key]); }); // 逐字段追加empHistory子对象属性,统一加empHistory.前缀匹配绑定规则 var empHistory = getEmploymentHistory(); Object.keys(empHistory).forEach(key => { formData.append(`empHistory.${key}`, empHistory[key]); }); $.ajax({ url: jspath + '/Document/PostDocument', data: formData, dataType: 'json', type: 'POST', cache: false, async: false, processData: false, contentType: false, success: function (data) { Swal.fire({ position: 'top-end', icon: 'success', title: 'Document added Successfully', showConfirmButton: false, timer: 1500 }).then(function () { $('#spinneraj').hide(); }) $('#modalAction').modal('hide'); GetCompanyDocument(); }, error: function (data) { Swal.fire({ position: 'top-end', icon: 'error', title: 'Oops...', text: data, }).then(function () { $('#spinneraj').hide(); }) } }).done(function () { $('#spinneraj').hide(); }); } function getDocumentObject() { var object = new Object(); var attachment = $('#AttachmentFile').val(); if (!attachment) { attachment = $('#fileHealthCardCopyPath').val(); } object.IssueDate = $('#IssueDate').val(); object.Title = $('#DocumentTitle').val(); object.HealthCardId = $('#HealthCardId').val(); object.ExpiryDate = $('#ExpiryDate').val(); object.Attachment = attachment; object.EntityTypeId = entityType; object.EntityRecId = id; return object; } function getEmploymentHistory() { var object = new Object(); object.Title = $('#cmdTitle').val(); object.NameSupervisor = $('#txtSupervisorName').val(); object.CompanyName = $('#txtCompanyName').val(); object.Designation = $('#cmbDesignation').val(); object.SalaryCurrency = $('#cmbSaleryCurrency').val(); object.BasicSalary = $('#txtBasicSalary').val(); object.DurationFrom = $('#IssueDate').val(); object.TillToDate = $('#ExpiryDate').val(); object.Accommodation = $('#txtAccommodation').val(); object.Transportation = $('#txtTransportation').val(); object.Food = $('#txtFood').val(); object.LastSalary = $('#txtLastSalary').val(); object.OtherAllowance1 = $('#txtOtherAllowance1').val(); object.PhoneNumber = $('#txtPhone').val(); object.ReasonLeaving = $('#txtReasonLeaving').val(); object.City = $('#txtCity').val(); object.Address = $('#txtAddress').val(); return object; }
注意:ajax配置中processData: false和contentType: false两个参数必须保留,前者会阻止jQuery对FormData做错误的URL编码处理,后者会让浏览器自动生成带正确分隔符的
multipart/form-data请求头,不要手动设置contentType为multipart/form-data,否则会丢失boundary标识导致文件绑定失败。原有配置里的enctype、traditional参数对FormData提交无作用,可以直接删除。
后端接口注意点
后端Action不要加[FromBody]特性,multipart/form-data类型请求走表单绑定逻辑,接口写法参考:
[HttpPost] public async Task<IActionResult> PostDocument(DocumentVM model) { // 此处可直接获取绑定完成的healthCard、empHistory、attachmentFile三个属性 // 业务逻辑自行实现 }
补充说明
- 带前缀逐字段追加FormData的写法完全匹配ASP.NET MVC默认模型绑定规则,不需要额外编写自定义模型绑定器
- 不建议把复杂对象转JSON字符串塞入FormData,除非后端额外编写逻辑读取表单中的字符串再手动反序列化,属于冗余实现
- 前端传入的日期、数值类型字段只要保持原有格式(日期为yyyy-MM-dd、数值为合法数字格式),默认绑定器可自动完成类型转换。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

