.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
相关产品推荐
相关产品推荐

