.NET Core Web App AJAX调用控制器Action抛出System.ArgumentNullException
你的代码出现ArgumentNullException以及移除日期ID后模型为null的问题,主要源于日期序列化/解析错误、模型绑定失败和缺少输入验证这几个关键点,下面逐一分析并给出修复方案:
核心问题拆解
无效日期导致模型绑定失败
当new Date($("#milestoneDate").val())解析输入的日期字符串失败时,会生成一个Invalid Date对象,JSON序列化后会变成null或者.NET Core无法识别的格式。而你的Milestone模型中Date是非空的DateTime类型,这直接触发模型绑定失败,抛出ArgumentNullException。
另外,模型里的DisplayFormat属性有语法错误:"{0:d"少了闭合的大括号,应该改为"{0:d}"。输入验证缺失
点击添加按钮前,没有检查milestoneText是否为空,也没有验证日期格式是否合法,导致无效数据直接被发送到后端。移除日期ID后的模型空值问题
移除日期输入框的ID后,$("#milestoneDate").val()获取不到值,date变量变为undefined,序列化后请求中缺少Date字段,同样因为模型的Date是非空类型,模型绑定完全失败,最终milestone参数为null。
分步修复方案
1. 修正模型的DisplayFormat属性
先把模型里的语法错误改掉:
namespace TIB.Models { public class Milestone { [Key] public int Id { get; set; } [DisplayName("")] [StringLength(30, ErrorMessage ="Milestone cannot exceed 30 characters.")] public string MilestoneName { get; set; } [DisplayName("Milestone Date")] [DisplayFormat(DataFormatString = "{0:d}")] // 修复闭合大括号 public DateTime Date { get; set; } [ForeignKey("ProjectId")] public int ProjectId { get; set; } } }
2. 前端添加严格的输入验证与日期处理
使用moment.js(你代码里已经用到了)来严格验证日期格式,避免无效日期被发送:
$("#addMilestoneBtn").on('click', function (e) { e.preventDefault(); $('#loading-overlay').fadeIn(); // 获取并清洗输入值 const milestoneText = $("#milestoneText").val().trim(); const dateStr = $("#milestoneDate").val().trim(); const projectId = $("#projectId").val(); // 验证里程碑文本不为空 if (!milestoneText) { $("#milestoneValidation").show(); $('#loading-overlay').fadeOut(); return false; } $("#milestoneValidation").hide(); // 用moment严格验证日期格式(匹配你设置的'l'格式) const date = moment(dateStr, 'L', true); if (!date.isValid()) { alert("请输入有效的日期格式"); $('#loading-overlay').fadeOut(); return false; } // 转换为ISO格式字符串,确保.NET能正确解析 const milestoneDetails = { Id: 0, MilestoneName: milestoneText, ProjectId: Number(projectId), Date: date.toISOString() }; const jsonData = JSON.stringify(milestoneDetails); $.ajax({ type: "POST", url: '/TIB/Forms/MilestoneAddorUpdate', data: jsonData, contentType: "application/json", dataType: "json", success: OnSuccess, error: OnErrorCall }); function OnSuccess(response) { if (response) { updateFormTables('milestoneTable'); const today = moment().format('l'); $("#milestoneDate").val(today); $("#milestoneText").val(""); $('#loading-overlay').fadeOut(); } } function OnErrorCall(response) { alert("Error. Milestone not added. " + response.responseText); $('#loading-overlay').fadeOut(); } return false; });
3. 后端增强模型验证与错误处理
把控制器Action的返回类型改为IActionResult,这样可以返回更明确的HTTP错误状态码,同时添加模型验证:
[HttpPost("~/Forms/MilestoneAddorUpdate")] public IActionResult MilestoneAddorUpdate([FromBody] Milestone milestone) { // 检查模型是否绑定成功、是否合法 if (milestone == null || !ModelState.IsValid) { return BadRequest("无效的请求数据,请检查输入"); } using (SqlConnection sqlConnection = new SqlConnection(_configuration.GetConnectionString("TIBDB"))) { sqlConnection.Open(); using (SqlCommand sqlCmd = new SqlCommand("usp_MilestoneAddorEdit", sqlConnection)) { sqlCmd.CommandType = CommandType.StoredProcedure; sqlCmd.Parameters.AddWithValue("Id", milestone.Id); sqlCmd.Parameters.AddWithValue("ProjectId", milestone.ProjectId); sqlCmd.Parameters.AddWithValue("Date", milestone.Date); sqlCmd.Parameters.AddWithValue("MilestoneName", milestone.MilestoneName); sqlCmd.ExecuteNonQuery(); } } // 返回Ok结果,携带布尔值 return Ok(true); }
4. 可选:允许日期为空(如果业务允许)
如果你的业务场景支持里程碑日期为空,可以把模型的Date属性改为可空类型:
public DateTime? Date { get; set; }
这样即使日期解析失败,模型绑定也不会完全失败,你可以在后端判断并处理空值情况。
内容的提问来源于stack exchange,提问作者NerdyDriod

