ASP.NET Core MVC如何实现用户名或邮箱均可登录的模型验证
方案1:使用自定义数据注解特性(符合DataAnnotations使用规范)
System.ComponentModel.DataAnnotations 命名空间下没有原生支持多属性二选一必填的内置特性,你可以继承ValidationAttribute自定义跨属性校验特性来实现需求,代码如下:
自定义校验特性代码
using System.ComponentModel.DataAnnotations; /// <summary> /// 指定的多个属性中至少填写一个非空值 /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class AtLeastOneRequiredAttribute : ValidationAttribute { private readonly string[] _targetProperties; public AtLeastOneRequiredAttribute(params string[] targetProperties) { _targetProperties = targetProperties; ErrorMessage = $"至少需要填写 {string.Join(" 或 ", _targetProperties)} 中的一项"; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // 遍历所有指定属性,只要有一个不为空就校验通过 foreach (var propName in _targetProperties) { var propInfo = validationContext.ObjectType.GetProperty(propName); if (propInfo == null) { return new ValidationResult($"不存在属性 {propName}"); } var propValue = propInfo.GetValue(validationContext.ObjectInstance); if (!string.IsNullOrWhiteSpace(propValue?.ToString())) { return ValidationResult.Success; } } // 所有属性都为空,校验失败 return new ValidationResult(ErrorMessage); } }
模型使用示例
因为是跨属性校验,所以特性直接加在LoginModel类上即可:
[AtLeastOneRequired(nameof(UserName), nameof(Email))] public class LoginModel { public string UserName { get; set; } public string Email { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } }
配置完成后ModelState.IsValid就会自动判断用户名和邮箱是否至少填了一个。
其他可行实现方案
- Controller手动校验:如果只有这一处需要二选一校验,可以直接在动作方法里加校验逻辑,不需要额外写自定义类:
优点是逻辑直观实现快,缺点是复用性差,多场景需要同规则时会产生重复代码。[HttpPost] public async Task<IActionResult> Login(LoginModel model) { if(string.IsNullOrWhiteSpace(model.UserName) && string.IsNullOrWhiteSpace(model.Email)) { ModelState.AddModelError(string.Empty, "用户名或邮箱至少填写一项"); } if(ModelState.IsValid) { // 后续业务逻辑 } return View(model); } - 合并输入字段优化体验:可以直接去掉模型里的两个字段,改成一个
LoginAccount字段,前端输入框提示为「用户名/邮箱」,后续查询用户时分别匹配用户名和邮箱即可:
这种方案对用户更友好,大部分主流网站都采用这种设计,同时省去了二选一校验的逻辑。// 改造后的模型 public class LoginModel { [Required(ErrorMessage = "用户名或邮箱不能为空")] public string LoginAccount { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } } // 后续查询用户逻辑示例(假设用Identity) var user = await _userManager.FindByNameAsync(model.LoginAccount) ?? await _userManager.FindByEmailAsync(model.LoginAccount); - 使用FluentValidation库:如果项目中有大量复杂校验规则,可以引入第三方校验库FluentValidation,用声明式语法写校验规则,更灵活易维护:
先安装FluentValidation.AspNetCore包,然后编写校验类:
在Program.cs注册服务后即可自动触发模型校验,不需要修改Controller原有逻辑。public class LoginModelValidator : AbstractValidator<LoginModel> { public LoginModelValidator() { RuleFor(x => x.Password).NotEmpty(); RuleFor(x => x.UserName).NotEmpty().When(x => string.IsNullOrWhiteSpace(x.Email)); RuleFor(x => x.Email).NotEmpty().When(x => string.IsNullOrWhiteSpace(x.UserName)) .WithMessage("用户名或邮箱至少填写一项"); } }
内容的提问来源于stack exchange,提问作者Sercan
相关产品推荐
相关产品推荐

