如何增强 ASP.NET 核心验证以忽略某些属性并验证作为该属性函数的值?

How to augment the ASP.NET Core validation to ignore some properties and validate on a value that is a function of that property?

提问人:Ilya Chernomordik 提问时间:10/11/2023 最后编辑:Ilya Chernomordik 更新时间:10/12/2023 访问量:58

问:

我有一堂课取自 Roslyn:https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/Optional.csOptional

它的要点:

public readonly struct Optional<T>
{
    public Optional(T value)
    {
        _hasValue = true;
        _value = value;
    }

    public bool HasValue => _hasValue;

    public T Value => _hasValue 
        ? _value
        : throw new InvalidOperationException("Optional object must have a value");
}

现在,我想在 ASP.NET 请求模型中使用此类,以便用户可以在更新请求期间设置该模型的任意属性以执行部分更新。

这本身工作正常,我得到了一个合适的模型,但是当我想添加验证(而不是重用标准验证)属性时,问题就出现了。

弹出这两个问题:

  1. 验证尝试读取并在未设置字段时引发异常(当它尝试访问子项时)Value
  2. 如果设置了该字段,则验证属性将无法正确获取,并会尝试验证结构本身Value

我不确定我是否试图正确解决它,但我想知道我是否可以以某种方式更改字段或包含此类字段的对象的验证,以便验证中忽略所有未设置的字段,并且设置的字段将验证而不是结构本身。Optional.Value

P.S. 我不能使用,因为我想区分空值和没有值。即具有 null 值的字段表示:将字段更新为 ,no field 表示 - 不要更改该字段。这是尝试对 json 中的未定义进行建模。因此,例如,如果相应的字段可以接受 null,我将有 Optional<string?> 和 Optional<long?>。Nullable<T>null

C# ASP.NET-Core 验证 .NET-CORE 模型绑定

评论

1赞 Nick Murphy 10/11/2023
听起来你对实现 IValidateableObject 很感兴趣,它允许你编写自定义代码来在反序列化时验证内容。重用标准验证可能很困难,但如果使用 Nullable<T> 而不是 Optional<T>则可以这样做,因为它更深入地嵌入到语言中。
0赞 Brando Zhang 10/12/2023
我也不明白此模型验证的 Nullable 和 optional 之间有什么区别。
0赞 Ilya Chernomordik 10/12/2023
我已经为为什么 Nullable 在这种情况下不起作用的问题添加了一些细节
1赞 Dai 10/12/2023
@BrandoZhang 数类型将允许程序区分 和 - C# 确实给了我们,但它只与 -types 兼容,不能用于表示 vs. ,因此为什么有许多第三方(甚至多个第一方)尝试实现类型,但 C#/ 中固有的局限性。NET的类型系统和泛型支持意味着它们都受到某种残酷的限制。Option<T>nullundefinedNullable<T>structundefinednullOption<T>

答:

0赞 Ale_Bianco 10/12/2023 #1

您可以尝试使用自定义验证属性:

  • 创建可以处理该类型的自定义验证属性。此属性将检查 是否具有值,如果有,它将验证 .Optional<T>Optional<T>Value
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class OptionalValidationAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var optional = value as dynamic; // Use dynamic to access HasValue and Value

        if (optional != null && optional.HasValue)
        {
            var validationResult = base.IsValid(optional.Value, validationContext);

            if (validationResult != ValidationResult.Success)
            {
                return validationResult;
            }
        }

        return ValidationResult.Success;
    }
}
  • 将 应用于请求模型中类型为 的属性。OptionalValidationAttributeOptional<T>
public class UpdateRequestModel
{
    [OptionalValidation]
    public Optional<string> Name { get; set; }

    [OptionalValidation]
    public Optional<int> Age { get; set; }
}
  • 现在,当您在 ASP.NET 操作中执行模型验证时,将应用于 类型的属性。它将检查 是否有值,如果有,它将验证 .OptionalValidationAttributeOptional<T>Optional<T>Value
[HttpPost]
public IActionResult Update([FromBody] UpdateRequestModel model)
{
    if (!ModelState.IsValid)
    {
        // Handle invalid model
        return BadRequest(ModelState);
    }

    // Process valid model
    // ...
}

评论

0赞 Ilya Chernomordik 10/12/2023
我不太明白在这种情况下如何进行真正的验证?只有当我想对每个可选字段应用完全相同的验证时,这才有效,但事实并非如此。