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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:21:30