如何通过Ajax向.NET Core MVC控制器传递含对象数组与文件数组的Product对象
问题场景
尝试通过Ajax将包含文件数组和对象数组的Product对象传递到.NET Core MVC控制器,提交后Product的其他属性及文件数据均正常接收,但Variants列表始终为空。
涉及模型代码
public class Product { public int Id { get; set; } public required string Name { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public int SubcategoryId { get; set; } public virtual Subcategory? Subcategory { get; set; } public virtual List<ProductImage> Images { get; set; } = new List<ProductImage>(); public virtual IEnumerable<ProductVariant> Variants { get; set; } = new List<ProductVariant>(); [NotMapped] public IEnumerable<IFormFile> FormFiles { get; set; } } public class ProductVariant { public int Id { get; set; } public int ProductId { get; set; } public required int Quantity { get; set; } public int MinQuantity { get; set; } = 1; public int? MaxQuantity { get; set; } public double? Width { get; set; } public double? Length { get; set; } public double? Height { get; set; } public double? Weight { get; set; } public required double Price { get; set; } public double Discount { get; set; } = 0; public ProductSize? Size { get; set; } // enum public Gender? Gender { get; set; } // enum public string? Color { get; set; } = string.Empty; public string? Material { get; set; } = string.Empty; public virtual Product? Product { get; set; } }
问题原因
- FormData无法直接绑定数组对象:直接将
variants数组append到FormData中,.NET模型绑定无法识别这种格式,需要按照键名[索引].属性名的格式逐个添加属性。 - 数据类型不匹配:输入框获取的值是字符串类型,未转换为模型对应的数字、枚举类型,导致绑定失败。
- 模型集合类型问题:Product类中Variants定义为
IEnumerable<ProductVariant>,这是只读集合,模型绑定需要可写的集合类型(如List)。
解决方案
1. 修改前端JavaScript代码
调整addProduct函数中FormData的构建逻辑,遍历Variants数组,按模型绑定要求的格式添加每个属性,并转换数据类型:
function addProduct() { let product = { name: document.getElementById("Name").value, description: document.getElementById("Description").value, subcategoryId: parseInt(document.getElementById("SubcategoryId").value), }; var files = Array.from($('#inputFiles')[0].files); var formData = new FormData(); // 添加Product基础属性 for (var key in product) { formData.append(key, product[key]); } // 添加Variants数组,按.NET模型绑定格式拼接键名 Variants.forEach((variant, index) => { // 转换数字类型 formData.append(`Variants[${index}].Quantity`, parseInt(variant.quantity) || 0); formData.append(`Variants[${index}].MinQuantity`, parseInt(variant.minQuantity) || 1); formData.append(`Variants[${index}].MaxQuantity`, variant.maxQuantity ? parseInt(variant.maxQuantity) : null); formData.append(`Variants[${index}].Width`, variant.width ? parseFloat(variant.width) : null); formData.append(`Variants[${index}].Length`, variant.length ? parseFloat(variant.length) : null); formData.append(`Variants[${index}].Height`, variant.height ? parseFloat(variant.height) : null); formData.append(`Variants[${index}].Weight`, variant.weight ? parseFloat(variant.weight) : null); formData.append(`Variants[${index}].Price`, parseFloat(variant.price) || 0); formData.append(`Variants[${index}].Discount`, parseFloat(variant.discount) || 0); // 枚举类型直接传递字符串,.NET会自动转换 formData.append(`Variants[${index}].Size`, variant.size); formData.append(`Variants[${index}].Gender`, variant.gender); formData.append(`Variants[${index}].Color`, variant.color); formData.append(`Variants[${index}].Material`, variant.material); }); // 添加文件 for (var key in files) { formData.append("formFiles", files[key]); } $.ajax({ url: "/Administration/Product/Create", type: 'POST', data: formData, dataType: "json", processData: false, contentType: false, success: function (data) { console.log(data); } }); }
同时,在addProductVariant函数中添加输入框清空逻辑,避免重复添加:
function addProductVariant() { let variant = { quantity: productQuantityInput.value, minQuantity: productMinuantityInput.value, maxQuantity: productMaxuantityInput.value, width: productWidthInput.value, length: productLengthInput.value, height: productHeightInput.value, weight: productWeightInput.value, price: productPriceInput.value, discount: productDiscountInput.value, color: productColorInput.value, material: productMaterialInput.value, size: productSize.value, gender: productGender.value }; Variants.push(variant); // 清空输入框 productQuantityInput.value = ''; productMinuantityInput.value = '1'; // 对应模型默认值 productMaxuantityInput.value = ''; productWidthInput.value = ''; productLengthInput.value = ''; productHeightInput.value = ''; productWeightInput.value = ''; productPriceInput.value = ''; productDiscountInput.value = '0'; // 对应模型默认值 productColorInput.value = ''; productMaterialInput.value = ''; productSize.selectedIndex = 0; productGender.selectedIndex = 0; }
2. 修改后端Product模型
将Variants的类型从IEnumerable<ProductVariant>改为List<ProductVariant>,确保模型绑定可以写入:
public class Product { // ... 其他属性不变 public virtual List<ProductVariant> Variants { get; set; } = new List<ProductVariant>(); // ... 其他属性不变 }
3. 控制器调试优化(可选)
添加模型状态校验,输出绑定错误信息便于调试:
public async Task<JsonResult> Create(Product product) { if (!ModelState.IsValid) { var errors = ModelState.Values.SelectMany(v => v.Errors.Select(e => e.ErrorMessage)); return Json(new { status = "Fail", errors = errors }); } UnitOfWork.ProductRepo.Add(product); await UnitOfWork.SaveAsync(); return Json("Success"); }
内容的提问来源于stack exchange,提问作者MiDo
相关产品推荐
相关产品推荐

