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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 00:13:16