.NET Core 7.0中Ajax Post提交Action时Model始终为空问题
问题:.NET Core 7.0中Ajax Post提交数据,Controller接收模型始终为null
在.NET Core 7.0开发场景下,通过Ajax Post向Controller的AddOrUpdateAddress Action提交数据时,无论是否添加[FromBody]特性,接收的AddressViewModel始终为null。
控制器Action方法
[HttpPost] public async Task<JsonResult> AddOrUpdateAddress([FromBody]AddressViewModel model) { var address = _mapper.Map<Address>(model); // 其他业务逻辑 }
Ajax请求代码
$.ajax({ url: "/Company/AddOrUpdateAddress", type: "POST", async: false, contentType: "application/json;", data: JSON.stringify(address), success: function (response) { company.details.address.addEdit.closeDetails(); $.notify('Saved', { position: "top right", className: "success" }); }, error: function (e) { $.notify('Error', { position: "top right", className: "error" }); return false; } });
AddressViewModel模型
public class AddressViewModel { public int Id { get; set; } [Display(Name = "Address Type")] public int? AddressTypeId { get; set; } public AddressType? Type { get; set; } public string? Address1 { get; set; } public string? Address2 { get; set; } public string? City { get; set; } [Display(Name = RegistrationTooltip.State)] public int? StateId { get; set; } public string? StateName { get; set; } [Display(Name = RegistrationTooltip.Country)] public int CountryId { get; set; } [Display(Name = "Zip")] public string? Zip { get; set; } public string? FullAddress => new Regex("[ ]{2,}", RegexOptions.None).Replace( $"{Address1?.Humanize(LetterCasing.Title)}, {Address2?.Humanize(LetterCasing.Title)}<br/> {City?.Humanize(LetterCasing.Title)} {(!StateId.HasValue ? StateName?.Humanize(LetterCasing.Title) : State?.Name?.Humanize(LetterCasing.Title))} - {Zip}<br/> <span><img src=\"/3rdParty/flags/blank.gif\" class=\"flag flag-{Country?.TwoLetterIsoCode}\" alt=\"{Country?.Name?.Humanize(LetterCasing.Title)}\"> {Country?.Name?.Humanize(LetterCasing.Title)}</span>", " "); public int? CompanyId { get; set; } [Display(Name = "Is Default")] public bool IsDefault { get; set; } [Display(Name = "Primary Hr Contact")] public string? PrimaryHrContactUserId { get; set; } [Display(Name = "Primary Finance Contact")] public string? PrimaryFinanceContactUserId { get; set; } [Display(Name = "Primary Facility Contact")] public string? PrimaryFacilityContactUserId { get; set; } [Display(Name = "Additional Hr Recipients")] public string? AdditionalHrRecipients { get; set; } [Display(Name = "Additional Finance Recipients")] public string? AdditionalFinanceRecipients { get; set; } [Display(Name = "Additional Facility Recipients")] public string? AdditionalFacilityRecipients { get; set; } public State? State { get; set; } public Country? Country { get; set; } public List<ShiftViewModel>? Shifts { get; set; } public List<OperatingHourViewModel>? OperatingHours { get; set; } public UserLiteDTO ? PrimaryHrContactUser { get; set; } public UserLiteDTO? PrimaryFinanceContactUser { get; set; } public UserLiteDTO? PrimaryFacilityContactUser { get; set; } public bool IsDeleted { get; set; } }
提交的JSON数据
{ "CountryId": "2", "AddressTypeId": "9", "Address1": "7th Floor, South Wing, ", "Address2": "Krishe Sapphire", "Zip": "500081", "StateId": "92", "City": "Hyderabad", "StateName": "", "Id": "607", "PrimaryHrContactUserId": "", "PrimaryFinanceContactUserId": "", "PrimaryFacilityContactUserId": "", "IsDefault": false, "IsDeleted": false, "OperatingHours": [ { "Id": 0, "Day": "Sunday", "OpeningTime": "", "ClosingTime": "", "IsClosed": true, "CompanyId":1, "AddressId":1 }, { "Id": 0, "Day": "Monday", "OpeningTime": "11:00 AM", "ClosingTime": "4:30 PM", "IsClosed": false, "CompanyId":1, "AddressId":1 }, { "Id": 0, "Day": "Tuesday", "OpeningTime": "", "ClosingTime": "", "IsClosed": false, "CompanyId":1, "AddressId":1 }, { "Id": 0, "Day": "Wednesday", "OpeningTime": "", "ClosingTime": "", "IsClosed": false, "CompanyId":1, "AddressId":1 }, { "Id": 0, "Day": "Thursday", "OpeningTime": "", "ClosingTime": "", "IsClosed": false, "CompanyId":1, "AddressId":1 }, { "Id": 0, "Day": "Friday", "OpeningTime": "", "ClosingTime": "", "IsClosed": false, "CompanyId":1, "AddressId":1 }, { "Id": 0, "Day": "Saturday", "OpeningTime": "", "ClosingTime": "", "IsClosed": false, "CompanyId":1, "AddressId":1 } ], "Shifts": [ { "Id": "0", "ShiftName": "Regular", "OpeningTime": "", "ClosingTime": "", "IsActive": false, "CompanyId": 1, "AddressId":1 }, { "Id": "0", "ShiftName": "Morning", "OpeningTime": "", "ClosingTime": "", "IsActive": false, "CompanyId": 1, "AddressId":1 }, { "Id": "0", "ShiftName": "Night", "OpeningTime": "", "ClosingTime": "", "IsActive": false, "CompanyId": 1, "AddressId":1 } ] }
复现步骤
- 在Visual Studio中运行上述代码
- 使用PostMan或其他REST工具,以上述JSON为请求体发起HTTPS Post请求
解决方案
1. 修复JSON数据类型不匹配问题
提交的JSON中多个数值类型字段被序列化为字符串(如"CountryId": "2"、"AddressTypeId": "9"),但ViewModel中这些字段是int/int?类型,.NET Core默认JSON序列化器不会自动转换字符串到数值,导致模型绑定失败。
处理方式:
- 前端传递JSON时,将数值类型字段改为数值格式,比如
"CountryId": 2、"AddressTypeId":9; - 或在ViewModel的数值字段上添加
[JsonConverter(typeof(JsonStringEnumConverter))]特性(需引用System.Text.Json.Serialization命名空间),允许序列化器转换字符串到数值。
2. 修正Ajax的contentType参数
Ajax代码中contentType: "application/json;"多了一个分号,正确格式应为contentType: "application/json",格式错误会影响Content-Type头的识别,导致模型绑定失败。
修改后的Ajax代码:
$.ajax({ url: "/Company/AddOrUpdateAddress", type: "POST", async: false, contentType: "application/json", data: JSON.stringify(address), success: function (response) { company.details.address.addEdit.closeDetails(); $.notify('Saved', { position: "top right", className: "success" }); }, error: function (e) { $.notify('Error', { position: "top right", className: "error" }); return false; } });
3. 检查嵌套模型的可访问性
确保ShiftViewModel、OperatingHourViewModel等嵌套模型的属性为public,且有无参数构造函数,否则模型绑定无法实例化这些嵌套对象。
4. 启用模型绑定错误日志排查
在Program.cs中添加配置,开启详细模型绑定错误日志:
builder.Services.AddControllersWithViews() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; }) .AddMvcOptions(options => { options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "该字段为必填项"); });
同时在Action中添加模型验证,输出错误信息:
[HttpPost] public async Task<JsonResult> AddOrUpdateAddress([FromBody]AddressViewModel model) { if (!ModelState.IsValid) { var errors = ModelState.Values.SelectMany(v => v.Errors) .Select(e => e.ErrorMessage); return Json(new { success = false, errors = errors }); } var address = _mapper.Map<Address>(model); // 其他业务逻辑 }
内容的提问来源于stack exchange,提问作者HaBo
相关产品推荐
相关产品推荐

