Asp.Net Core模型绑定问题:Ajax提交列表数据无法映射
解决ASP.NET Core中JQuery Ajax提交列表数据无法绑定到模型的问题
问题背景
在ASP.NET Core Web应用中,使用JQuery Ajax提交表单变更时,由于可编辑表格没有常规输入字段,需要手动向请求中追加ListProductSubCategories数据,但服务器端模型始终无法获取该列表值,收到的是空列表。
模型定义
public class ProductCategoryModel { public ProductCategoryDto Category { get; set; } = new(); } public class ProductCategoryDto { public string Description { get; set; } = default!; public List<ProductSubCategoryDto> ListProductSubCategories { get; set; } = new(); } public class ProductSubCategoryDto { public Guid? Id { get; set; } public string Description { get; set; } = default!; }
原有效请求格式
仅提交单个字段时,模型绑定正常工作:
[{"name":"Model.Category.Description","value":"CATEGORY"}]
失败的尝试方式
方式一:直接追加数组对象
[{"name":"Model.Category.Description","value":"CATEGORY"}, {"name":"Model.Category.ListProductSubCategories", "value":[ {"Id":null,"Description":"Nouvelle sous-catégorie"}, {"Id":null,"Description":"Nouvelle sous-catégorie 2"} ] }]
方式二:重复追加同一键名
[{"name":"Model.Category.Description","value":"CATEGORY"}, {"name":"Model.Category.ListProductSubCategories","value":{"Id":null,"Description":"Nouvelle sous-catégorie"}}, {"name":"Model.Category.ListProductSubCategories","value":{"Id":null,"Description":"Nouvelle sous-catégorie 2"}}]
原因分析
ASP.NET Core的模型绑定对application/x-www-form-urlencoded格式(即serializeArray生成的表单编码格式)有特定要求:列表数据必须通过索引下标标识每个元素的属性,不能直接传递数组对象或重复使用同一键名。这种格式下,列表元素的键名需要形如Model.Category.ListProductSubCategories[0].Description。
解决方案
方案一:生成带索引的表单键值对
修改客户端代码,遍历子分类列表,生成带索引的键值对并逐个添加到请求数据中:
function mapAssociatedToSimpleArray(obj) { var a3 = Object.keys(obj).map(function (k) { return obj[k]; }) return a3; } var data = $("#form").serializeArray(); var listSubCategories = mapAssociatedToSimpleArray(_dataSource); // 遍历子分类,生成带索引的键值对 listSubCategories.forEach(function(subCat, index) { data.push({ name: `Model.Category.ListProductSubCategories[${index}].Id`, value: subCat.Id }); data.push({ name: `Model.Category.ListProductSubCategories[${index}].Description`, value: subCat.Description }); }); $.ajax({ url: "/inv/ProductsCategoriesController/Edit/@Model.Categorie.Id", type: 'POST', dataType: 'json', data: data, headers: {'RequestVerificationToken': $(".AntiForge input").val() }, success: function (data) { // 处理成功逻辑 }, error: function (xhr, ajaxOptions, thrownError) { if (utils.hasJsonStructure(xhr.responseText)) { $("#form").handleModelErrors(JSON.parse(xhr.responseText)); } notifications.error(); } });
生成的请求数据示例:
[{"name":"Model.Category.Description","value":"CATEGORY"}, {"name":"Model.Category.ListProductSubCategories[0].Id","value":null}, {"name":"Model.Category.ListProductSubCategories[0].Description","value":"Nouvelle sous-catégorie"}, {"name":"Model.Category.ListProductSubCategories[1].Id","value":null}, {"name":"Model.Category.ListProductSubCategories[1].Description","value":"Nouvelle sous-catégorie 2"}]
方案二:直接提交JSON格式数据
如果不想处理表单编码的索引格式,可以将整个数据序列化为JSON字符串,设置请求头为application/json,让模型绑定直接解析JSON:
// 将表单数据转为对象 var formData = $("#form").serializeArray(); var model = {}; formData.forEach(function(item) { // 解析嵌套属性路径 var keys = item.name.split('.'); var current = model; for(var i = 0; i < keys.length; i++) { if(i === keys.length - 1) { current[keys[i]] = item.value; } else { current[keys[i]] = current[keys[i]] || {}; current = current[keys[i]]; } } }); // 添加子分类列表 model.Category.ListProductSubCategories = mapAssociatedToSimpleArray(_dataSource); $.ajax({ url: "/inv/ProductsCategoriesController/Edit/@Model.Categorie.Id", type: 'POST', dataType: 'json', contentType: 'application/json', // 关键:指定JSON内容类型 data: JSON.stringify(model), // 序列化为JSON字符串 headers: {'RequestVerificationToken': $(".AntiForge input").val() }, success: function (data) { // 处理成功逻辑 }, error: function (xhr, ajaxOptions, thrownError) { if (utils.hasJsonStructure(xhr.responseText)) { $("#form").handleModelErrors(JSON.parse(xhr.responseText)); } notifications.error(); } });
内容的提问来源于stack exchange,提问作者M.Parent
相关产品推荐
相关产品推荐

