.NET Core Web API如何仅对JSON提供的字段应用数据注解验证
.NET Core Web API 实现部分字段更新的条件验证方案
针对更新用户资料时仅验证JSON中实际提供字段的需求,这里提供几种实用的适配方案:
方案1:创建更新专用DTO + 自定义验证特性
原有UserDetails模型是为全量场景设计的,更新场景建议单独定义DTO,避免污染原有模型。同时通过自定义验证特性实现「提供则必填」的逻辑:
1.1 自定义验证特性
public class IfProvidedRequiredAttribute : ValidationAttribute { protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { // 字段未提供(值为null)时跳过验证 if (value == null) return ValidationResult.Success; // 字符串类型检查是否为空/空白 if (value is string str && string.IsNullOrWhiteSpace(str)) return new ValidationResult(ErrorMessage ?? $"提供{validationContext.DisplayName}字段时不能为空"); return ValidationResult.Success; } }
1.2 更新专用DTO
public class UpdateUserDto { [Required(AllowEmptyStrings = false, ErrorMessage = "user_id为必填字段")] [JsonProperty("user_id")] public string? UserId { get; set; } [IfProvidedRequired(ErrorMessage = "提供home_phone字段时不能为空")] [JsonProperty("home_phone")] public string? HomePhone { get; set; } [IfProvidedRequired(ErrorMessage = "提供work_phone字段时不能为空")] [JsonProperty("work_phone")] public string? WorkPhone { get; set; } [IfProvidedRequired(ErrorMessage = "提供mobile_phone字段时不能为空")] [JsonProperty("mobile_phone")] public string? MobilePhone { get; set; } }
1.3 控制器中使用
[HttpPut("profile")] public IActionResult UpdateProfile([FromBody] UpdateUserDto dto) { if (!ModelState.IsValid) return BadRequest(ModelState); // 后续执行用户资料更新逻辑(注意UserId仅作查询用,不修改) return Ok("更新成功"); }
这种方式完全依赖.NET Core的自动模型验证,代码简洁且符合分层设计原则。
方案2:直接在控制器中手动验证(无需修改模型)
如果不想新增DTO,可以直接在控制器中读取原始JSON,结合模型绑定结果做针对性验证:
[HttpPut("profile")] public async Task<IActionResult> UpdateProfile([FromBody] UserDetails model) { // 先验证UserId必填项 if (string.IsNullOrWhiteSpace(model.UserId)) { ModelState.AddModelError("user_id", "user_id为必填字段"); return BadRequest(ModelState); } // 重读请求体获取原始JSON Request.EnableBuffering(); Request.Body.Position = 0; var rawJson = await new StreamReader(Request.Body).ReadToEndAsync(); var jsonObj = JObject.Parse(rawJson); // 检查每个可选字段是否在JSON中存在,存在则验证非空 if (jsonObj.ContainsKey("home_phone") && string.IsNullOrWhiteSpace(model.HomePhone)) ModelState.AddModelError("home_phone", "提供home_phone字段时不能为空"); if (jsonObj.ContainsKey("work_phone") && string.IsNullOrWhiteSpace(model.WorkPhone)) ModelState.AddModelError("work_phone", "提供work_phone字段时不能为空"); if (jsonObj.ContainsKey("mobile_phone") && string.IsNullOrWhiteSpace(model.MobilePhone)) ModelState.AddModelError("mobile_phone", "提供mobile_phone字段时不能为空"); if (!ModelState.IsValid) return BadRequest(ModelState); // 后续更新逻辑 return Ok("更新成功"); }
注意:需要在Program.cs中禁用默认的模型验证过滤器,避免提前返回错误:
builder.Services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; });
方案对比
- 方案1更推荐,通过DTO分离场景,代码可复用性高,维护成本低;
- 方案2适合快速临时处理,但耦合性较高,不适合复杂场景。
内容的提问来源于stack exchange,提问作者PavanKumar GVVS
相关产品推荐
相关产品推荐

