You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 11:10:40