.NET 7 Web API接收POST集合参数始终为Null问题排查
问题分析与解决方案
核心原因
你遇到的问题源于**[FromForm]模型绑定对复杂列表类型的格式要求严格**,同时TimeOnly和DayOfWeek类型的表单绑定需要特定输入格式,前端提交的格式不匹配导致列表字段无法被正确解析。
解决方案
方案1:按表单数组格式提交(推荐,无需修改后端模型)
当使用[FromForm]接收带文件的请求时,前端必须用multipart/form-data格式提交,且列表字段需遵循索引式命名规则,具体操作如下:
- 前端构建FormData:
const formData = new FormData(); // 添加基础字段 formData.append('ServiceId', serviceId ?? ''); formData.append('UserId', userId); formData.append('CategoryId', categoryId); formData.append('Title', title); formData.append('Description', description); formData.append('Price', price); // ...其他基础字段 // 处理Skills列表 skills.forEach((skill, index) => { formData.append(`Skills[${index}].SkillId`, skill.skillId ?? ''); formData.append(`Skills[${index}].Name`, skill.name); }); // 处理Availabilities列表(注意模型属性是DayOfWeeks,需严格对应) availabilities.forEach((avail, index) => { // DayOfWeeks可传枚举值(如1=Monday)或枚举名称(如"Monday") formData.append(`Availabilities[${index}].DayOfWeeks`, avail.dayOfWeek); // TimeOnly必须传"HH:mm:ss"格式的字符串 formData.append(`Availabilities[${index}].StartDate`, avail.startTime); // 例:"09:00:00" formData.append(`Availabilities[${index}].EndDate`, avail.endTime); // 例:"17:00:00" }); // 添加文件 servicesImages.forEach(file => { formData.append('ServicesImages', file); }); // 发送请求 await fetch('/api/services/upsert', { method: 'POST', body: formData });
- 后端保持现有模型和控制器代码不变,ASP.NET Core的
[FromForm]绑定器会自动解析这种格式的列表字段。
方案2:将列表序列化为JSON字符串提交(适合简化前端逻辑的场景)
如果不想处理表单索引命名,可以将列表转成JSON字符串作为单独表单字段提交,后端手动反序列化:
- 前端调整FormData构建:
const formData = new FormData(); // 添加基础字段... // 将列表转为JSON字符串 formData.append('SkillsJson', JSON.stringify(skills)); formData.append('AvailabilitiesJson', JSON.stringify(availabilities)); // 添加文件...
- 后端创建DTO接收表单数据,再转换为原模型:
// 新增表单接收DTO public class ServicesRequestFormDto { public int? ServiceId { get; set; } public int UserId { get; set; } public int CategoryId { get; set; } public int? AddressId { get; set; } public int ServiceTypeId { get; set; } public string Title { get; set; } public string Description { get; set; } public decimal Price { get; set; } public string SkillsJson { get; set; } public string AvailabilitiesJson { get; set; } public IFormFileCollection ServicesImages { get; set; } } // 控制器修改 public async Task<ActionResult<ServicesResponse>> UpsertServicesAsync([FromForm] ServicesRequestFormDto formDto) { // 反序列化列表字段 var skills = JsonSerializer.Deserialize<List<SkillRequest>>(formDto.SkillsJson); var availabilities = JsonSerializer.Deserialize<List<AvailabilityRequest>>(formDto.AvailabilitiesJson); // 组装原业务模型 var servicesRequest = new ServicesRequest { ServiceId = formDto.ServiceId, UserId = formDto.UserId, CategoryId = formDto.CategoryId, AddressId = formDto.AddressId, ServiceTypeId = formDto.ServiceTypeId, Title = formDto.Title, Description = formDto.Description, Price = formDto.Price, Skills = skills, Availabilities = availabilities, ServicesImages = formDto.ServicesImages }; // 后续业务逻辑处理... }
额外注意事项
TimeOnly类型:表单提交必须传递HH:mm:ss格式的字符串,不能直接传Date对象或其他格式。DayOfWeek枚举:支持传递枚举名称(如"Monday")或对应整数值(如1),两种格式绑定器均可识别。- 属性名称一致性:前端表单字段名称必须和后端模型属性名称完全匹配(比如模型里是
DayOfWeeks,前端不能写成DayOfWeek)。
内容的提问来源于stack exchange,提问作者Francisco Ojeda CL
相关产品推荐
相关产品推荐

