ASP.NET Core Razor Pages模型验证:邮箱/用户名二选一必填实现
ASP.NET Core 实现“邮箱/用户名二选一且不可同时填写”的模型验证
要实现必须填写邮箱或用户名其中一个,但不可同时填写的验证逻辑,普通的[Required]属性无法满足(它会强制要求两个字段都填写),可以通过以下两种方式实现:
方法一:让模型实现 IValidatableObject 接口
这种方式适合仅在当前模型使用的特定验证逻辑,直接在模型类内编写验证规则:
public class RegisterModel : IValidatableObject { public string? Email { get; set; } public string? Username { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { bool hasEmail = !string.IsNullOrWhiteSpace(Email); bool hasUsername = !string.IsNullOrWhiteSpace(Username); // 两个字段都为空时返回错误 if (!hasEmail && !hasUsername) { yield return new ValidationResult( "必须填写邮箱或用户名中的一个", new[] { nameof(Email), nameof(Username) } ); } // 两个字段都填写时返回错误 if (hasEmail && hasUsername) { yield return new ValidationResult( "不可同时填写邮箱和用户名", new[] { nameof(Email), nameof(Username) } ); } } }
方法二:自定义可复用的验证属性
如果多个模型需要类似的“二选一且互斥”验证逻辑,可以自定义一个ValidationAttribute,实现规则复用:
1. 编写自定义验证属性
public class EitherOneNotBothAttribute : ValidationAttribute { private readonly string _otherPropertyName; // 构造函数传入需要对比的另一个属性名称 public EitherOneNotBothAttribute(string otherPropertyName) { _otherPropertyName = otherPropertyName; } protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { // 获取对比属性的元数据 var otherProperty = validationContext.ObjectType.GetProperty(_otherPropertyName); if (otherProperty == null) { return new ValidationResult($"找不到指定属性:{_otherPropertyName}"); } // 获取对比属性的实际值 var otherValue = otherProperty.GetValue(validationContext.ObjectInstance); bool currentIsEmpty = string.IsNullOrWhiteSpace(value?.ToString()); bool otherIsEmpty = string.IsNullOrWhiteSpace(otherValue?.ToString()); // 两个字段都不为空的情况 if (!currentIsEmpty && !otherIsEmpty) { return new ValidationResult( "不可同时填写当前字段和指定字段", new[] { validationContext.MemberName, _otherPropertyName } ); } // 两个字段都为空的情况 if (currentIsEmpty && otherIsEmpty) { return new ValidationResult( "必须填写当前字段或指定字段中的一个", new[] { validationContext.MemberName, _otherPropertyName } ); } // 验证通过 return ValidationResult.Success; } }
2. 在模型中应用自定义属性
public class RegisterModel { [EitherOneNotBoth(nameof(Username))] public string? Email { get; set; } [EitherOneNotBoth(nameof(Email))] public string? Username { get; set; } }
控制器与视图的验证触发
在控制器的POST方法中,通过ModelState.IsValid判断验证是否通过:
[HttpPost] public IActionResult Register(RegisterModel model) { if (!ModelState.IsValid) { // 验证失败,返回视图显示错误信息 return View(model); } // 验证通过,执行注册逻辑 return RedirectToAction("Index"); }
视图中可通过标签助手显示验证错误:
<form asp-action="Register"> <div class="form-group"> <label asp-for="Email"></label> <input asp-for="Email" class="form-control" /> <span asp-validation-for="Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Username"></label> <input asp-for="Username" class="form-control" /> <span asp-validation-for="Username" class="text-danger"></span> </div> <!-- 显示所有验证错误汇总 --> <div asp-validation-summary="All" class="text-danger"></div> <button type="submit" class="btn btn-primary">注册</button> </form>
内容的提问来源于stack exchange,提问作者MaxMoore
相关产品推荐
相关产品推荐

