首页 > 解决方案 > 用于自定义验证的 ValidationAttribute 无法检查 null 或空(服务器端)

问题描述

我发现当属性不是调用查询的一部分而是方法签名的一部分时,永远不会调用自定义 ValidationAttribute 的 IsValid() 方法。

所以,我想知道RequiredAttribute 如何对QueryString 的某些参数起作用。我查看了RequiredAttribute 源代码,它会遇到与我将描述的相同的问题,

例如,使用以下操作方法签名:

    public ActionResult<IEnumerable<SpaceBody>> Get(
        [FromQuery]
        [SomeAttribute]string some,
        int minDistance, int maxDistance)

IsValid 方法将通过以下查询调用:

但不会被调用:

直到这里它才有意义:如果查询中没有参数,框架如何知道要应用哪个过滤器?好吧,它应该通过动作方法签名知道

如果我将签名更改为:

    public ActionResult<IEnumerable<SpaceBody>> Get(
        [FromQuery]
        [Required, Some]string some,
        int minDistance, int maxDistance)

自定义属性的 IsValid 方法也会触发:

那么,RequiredAttribute 做了什么来使另一个属性的 IsValid() 方法被调用呢?

如果我查看 github 代码,它没有任何我能注意到的东西:https ://github.com/microsoft/referencesource/blob/master/System.ComponentModel.DataAnnotations/DataAnnotations/RequiredAttribute.cs

在我的自定义属性上,我执行以下操作:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class SomeAttribute : ValidationAttribute
{
    private const string SOME_REGEX = @"(?i)^\b(xx)\b$";

    private const string SOME_NOT_VALID_ERROR_MESSAGE = "Some is not valid.";
    private const string SOME_REQUIRED_ERROR_MESSAGE = "Some must have a value.";

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var some = Convert.ToString(value);

        if (string.IsNullOrWhiteSpace(some))
            return new ValidationResult(SOME_REQUIRED_ERROR_MESSAGE);

        if (!Regex.Match(some, SOME_REGEX, RegexOptions.IgnoreCase).Success)
            return new ValidationResult(SOME_NOT_VALID_ERROR_MESSAGE);

        return ValidationResult.Success;
    }
}

它包含拒绝空值的逻辑(如RequiredAttribute 所做的那样),因此如果我使用 [Required, Some] 它将显示两次任何 null 或空参数调用,这是不希望的。

如果我只保留 [Some],则会跳过 null 或空错误,这是可能发生的最糟糕的事情。

我现在看到的唯一解决方案是从自定义属性中删除 null 或空逻辑并使用 [Required, Some]。好的,这可能是一个解决方案,但是因为动机错误:“因为我无法在 null 或空值的情况下触发自定义属性”。是设计使然吗?在这种情况下,RequiredAttribute 背后的魔力是什么让其他属性被触发?

为了完成上下文,这是我的 Startup.ConfigureServices:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvcCore(o =>
        {
            //o.Filters.Add<ValidateModelAttribute>();
            o.EnableEndpointRouting = false;
        })
        .AddApiExplorer()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
        .AddJsonFormatters()
        .AddDataAnnotations()
        .AddControllersAsServices();
    }

如果没有 SetCompatibilityVersion,它根本无法工作,但我们的生产代码已经有了它。

请注意,我也有一个 ValidateModelAttribute ,它只是:通过取消注释它,我没有得到任何不同的行为。

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

对不起,细节很长,最后我只想知道如何只使用 [Some] 来执行所有“some”相关的验证?

标签: c#asp.net-coreasp.net-web-api

解决方案


继承自,RequiredAttribute因为继承自ValidationAttribute

public class SomeAttribute :RequiredAttribute
{
    private const string SOME_REGEX = @"(?i)^\b(xx)\b$";

    private const string SOME_NOT_VALID_ERROR_MESSAGE = "Some is not valid.";
    private const string SOME_REQUIRED_ERROR_MESSAGE = "Some must have a value.";

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var some = Convert.ToString(value);

        if (string.IsNullOrWhiteSpace(some))
            return new ValidationResult(SOME_REQUIRED_ERROR_MESSAGE);

        if (!Regex.Match(some, SOME_REGEX, RegexOptions.IgnoreCase).Success)
            return new ValidationResult(SOME_NOT_VALID_ERROR_MESSAGE);

        return ValidationResult.Success;
    }
}

推荐阅读