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

客户端Blazor自定义RequiredIf特性客户端验证失效如何解决

Blazor客户端自定义RequiredIf验证失效修复

现有代码结构

  • 父页面Blazor组件代码:
<EditForm Model="@MyModel" OnValidSubmit="@HandleValidSubmit">
  <DataAnnotationsValidator />
  ...
  <MyComponent DataModel="@MyModel.Transaction" />
</EditForm>
@code {
    private void HandleValidSubmit()
    {
    }
}
  • 子组件MyComponent.razor代码:
...
<InputText @bind-Value="@DataModel.InputValue" disabled="@DataModel.TextboxDisabled" />

@code {
   [Parameter]
   public MyModel DataModel { get; set; }
}

注:原代码中InputText绑定的@Model和参数名DataModel不匹配,会直接编译报错,上述代码已修正绑定路径。

  • 模型类MyModel.cs定义:
[RequiredIf(nameof(TextBoxEnabled), true)]
public string InputValue { get; set; }

public bool TextBoxEnabled { get; set; }

public bool TextBoxDisabled => !TextBoxEnabled;
  • 自定义验证特性RequiredIfAttribute.cs实现:
public sealed class RequiredIfAttribute : ValidationAttribute
{
    private readonly string propertyName;
    private readonly object? isValue;
    private readonly bool inverse;

    /// <summary>
    /// Initializes a new instance of the <see cref="RequiredIfAttribute"/> class.
    /// </summary>
    /// <param name="propertyName">Name of the depending property.</param>
    /// <param name="isValue">Required value. If <see cref="propertyName"/> is <see cref="isValue"/> then the property is not required.</param>
    /// <param name="inverse">If set to true, the value is not required when <see cref="propertyName"/> is
    /// not <see cref="isValue"/>.</param>
    public RequiredIfAttribute(string propertyName, object? isValue, bool inverse = false)
    {
      this.propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
      this.isValue = isValue;
      this.inverse = inverse;
    }

    /// <inheritdoc />
    public override string FormatErrorMessage(string name)
    {
      var inverseString = !inverse ? string.Empty : "not ";
      var errorMessage = $"Property '{name}' is required when '{propertyName}' is {inverseString}'{isValue}'";
      return ErrorMessage ?? errorMessage;
    }

    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
      var isRequired = IsRequired(validationContext, propertyName, isValue, inverse);
      if (!isRequired)
      {
        return ValidationResult.Success;
      }

      var validationResult = value == null
          ? new ValidationResult(FormatErrorMessage(validationContext.DisplayName))
          : ValidationResult.Success;
      return validationResult;
    }

    private static bool IsRequired(
        ValidationContext? validationContext,
        string propertyName,
        object? requiredValue,
        bool invert)
    {
      ArgumentNullException.ThrowIfNull(validationContext);
      ArgumentNullException.ThrowIfNull(propertyName);

      var owningType = validationContext.ObjectType;
      var property = owningType.GetProperty(propertyName);

      if (property == null)
      {
        throw new NotSupportedException($"Can't find {propertyName} on searched type: {owningType.Name}");
      }

      var requiredIfTypeActualValue = property.GetValue(validationContext.ObjectInstance);

      if (requiredIfTypeActualValue == null && requiredValue != null)
      {
        return false;
      }

      if (!invert)
      {
        return requiredIfTypeActualValue == null || requiredIfTypeActualValue.Equals(requiredValue);
      }

      return requiredIfTypeActualValue == null || !requiredIfTypeActualValue.Equals(requiredValue);
    }
}

问题现象

当文本框切换为启用状态、输入内容为空时点击提交按钮,HandleValidSubmit方法仍会被执行;预期此时基于TextBoxEnabled标识的RequiredIf验证规则应判定验证失败,阻止提交方法执行。

失效原因

  • 空值判断逻辑漏洞:现有RequiredIf特性仅判定null为无效值,而Blazor的InputText组件绑定空输入时返回的是空字符串"",不属于null,会直接判定为验证通过。
  • 验证触发逻辑限制:内置DataAnnotationsValidator仅在字段自身值变更、表单首次提交时触发对应字段的验证,切换TextBoxEnabled状态时,InputValue字段值未发生变化,不会自动重新执行验证规则,会直接复用之前缓存的验证通过结果。
  • 子组件绑定笔误:原代码子组件内InputText绑定的对象名和传入的Parameter参数名不匹配,会导致编译失败或绑定不生效。

修复方案

  1. 修复RequiredIf特性的空值判断逻辑
    找到IsValid方法内的验证结果判断代码,替换为以下逻辑,将空字符串、全空格字符串都纳入无效值范围:
// 替换原有value == null的判断逻辑
if (value == null || (value is string strVal && string.IsNullOrWhiteSpace(strVal)))
{
    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
  1. 处理关联属性变更的重验证触发
    两种实现方式二选一即可:
  • 方式一:全局重验证。在TextBoxEnabled属性的set方法中,或者切换文本框启用状态的交互逻辑中,调用EditContext的Validate方法全量重跑表单验证。首先需要在父页面持有EditContext实例:
<EditForm EditContext="_editContext" OnValidSubmit="@HandleValidSubmit">
  <DataAnnotationsValidator />
  ...
  <MyComponent DataModel="@MyModel.Transaction" />
</EditForm>
@code {
    private EditContext _editContext;
    private MyModel MyModel = new();

    protected override void OnInitialized()
    {
        _editContext = new EditContext(MyModel);
    }

    // 切换文本框状态的方法
    private void ToggleTextboxState(bool enabled)
    {
        MyModel.Transaction.TextBoxEnabled = enabled;
        // 触发全表单验证
        _editContext.Validate();
    }

    private void HandleValidSubmit()
    {
        // 提交逻辑
    }
}
  • 方式二:单字段重验证。如果不需要全量验证,仅需在TextBoxEnabled变更时通知验证系统重新校验InputValue字段即可,替换上述ToggleTextboxState方法内的_editContext.Validate()为:
_editContext.NotifyFieldChanged(_editContext.Field(nameof(MyModel.InputValue)));
  1. 修正子组件绑定
    将子组件内InputText的绑定路径从@Model.InputValue改为@DataModel.InputValue,和Parameter参数名保持一致。

内容的提问来源于stack exchange,提问作者Snake Eyes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:18:22