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

.NET 6数据注解验证失效:publicId为空无报错如何解决?

问题描述

在.NET 6.0中创建了带数据注解的模型类,同时编写了ApiRequestValidation扩展类用于检查请求体JSON并返回验证结果,但测试时发现当嵌套对象Appointment的publicId为空字符串或null时,无法触发对应的验证错误。

模型类与验证扩展类

using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;

public class ExternalNotificationRequest
{
    /// <summary>
    /// Appointment details
    /// </summary>
    [Required(AllowEmptyStrings = false, ErrorMessage = "Please provide a appointment object values.")]
    [JsonProperty("appointment")]
    public Appointment? Appointment { get; set; }
}

public class Appointment
{
    /// <summary>
    /// Notification type such as Create/Update/Delete
    /// </summary>
    [JsonProperty("type")]
    [Required(AllowEmptyStrings = false, ErrorMessage = "Please provide a appointment type. Valid values are  CREATE,UPDATE,DELETE.")]
    public string? Type { get; set; }

    /// <summary>
    /// appointment public id
    /// </summary>
    [JsonProperty("publicId")]
    [MaxLength(64)]
    [MinLength(64)]
    [Required(AllowEmptyStrings = false, ErrorMessage = "Please provide an appointment public id.")]
    public string? PublicId { get; set; }
}

public static class ApiRequestValidation
{
    public static bool IsValid(this object request, out ICollection<ValidationResult> validationResults)
    {
        validationResults = new List<ValidationResult>();
        return Validator.TryValidateObject(request, new ValidationContext(request), validationResults, true);
    }
}

Azure函数中的使用代码

ExternalNotificationRequest externalNotificationRequest = new ExternalNotificationRequest();
_logger.LogInformation("{FunctionName} Request Body: {requestBody}", functionName, requestBody);

externalNotificationRequest = 
    JsonConvert.DeserializeObject<ExternalNotificationRequest>(requestBody) 
        ?? new ExternalNotificationRequest();

// Validate Data Annotations.
if (!externalNotificationRequest.IsValid(validationResults: out var validationResults))
{
    return messageExtensions.RenderApiMessage(_logger, StatusCodes.Status400BadRequest, validationResults.Select(s => s.ErrorMessage).FirstOrDefault(), functionName, validationResults.Select(s => s.MemberNames.FirstOrDefault()).FirstOrDefault().ToString());
}

测试用JSON

{
    "appointment": {
        "type": "create",
        "publicId": ""
    }
}
问题原因

Validator.TryValidateObject方法不会自动递归验证嵌套的复杂对象属性。即使设置了第四个参数validateAllProperties: true,它也只会验证当前对象(这里是ExternalNotificationRequest)的直接属性,不会深入检查Appointment对象内部的Type、PublicId等属性的注解规则。

当测试JSON中的publicId为空字符串时,虽然Appointment类的PublicId属性有Required(AllowEmptyStrings = false)等注解,但因为嵌套对象的验证没有被触发,所以不会返回对应的错误信息。

解决方案

修改ApiRequestValidation扩展类,添加递归验证嵌套对象的逻辑,确保所有层级的模型属性都能被验证:

using System.ComponentModel.DataAnnotations;
using System.Reflection;

public static class ApiRequestValidation
{
    public static bool IsValid(this object request, out ICollection<ValidationResult> validationResults)
    {
        validationResults = new List<ValidationResult>();
        var isValid = Validator.TryValidateObject(request, new ValidationContext(request), validationResults, true);
        
        // 递归验证所有嵌套的复杂对象属性
        foreach (var property in request.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var propertyValue = property.GetValue(request);
            if (propertyValue == null)
                continue;
            
            // 排除值类型、字符串、枚举等不需要递归的类型
            if (property.PropertyType.IsValueType || property.PropertyType == typeof(string) || property.PropertyType.IsEnum)
                continue;
            
            // 验证嵌套对象
            if (!propertyValue.IsValid(out var nestedResults))
            {
                isValid = false;
                // 合并嵌套对象的验证结果,补充成员名称前缀
                foreach (var result in nestedResults)
                {
                    var memberNames = result.MemberNames.Any() 
                        ? result.MemberNames.Select(m => $"{property.Name}.{m}") 
                        : result.MemberNames;
                    validationResults.Add(new ValidationResult(result.ErrorMessage, memberNames));
                }
            }
        }
        
        return isValid;
    }
}

说明

  • 新增的递归逻辑会遍历当前对象的所有公开实例属性,判断是否为需要验证的复杂对象
  • 对每个非空的复杂对象调用IsValid方法进行验证
  • 将嵌套对象的验证结果合并到顶层结果中,同时为成员名称添加父属性前缀(比如Appointment.PublicId),方便定位错误位置
  • 保持原有验证逻辑的同时,补充了嵌套对象的验证覆盖

这样修改后,测试JSON中的空publicId就会触发对应的Required和长度验证错误,返回预期的提示信息。

内容的提问来源于stack exchange,提问作者PavanKumar GVVS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:54:51