c# - 用于自定义验证的 ValidationAttribute 无法检查 null 或空(服务器端)
问题描述
我发现当属性不是调用查询的一部分而是方法签名的一部分时,永远不会调用自定义 ValidationAttribute 的 IsValid() 方法。
所以,我想知道RequiredAttribute 如何对QueryString 的某些参数起作用。我查看了RequiredAttribute 源代码,它会遇到与我将描述的相同的问题,
例如,使用以下操作方法签名:
public ActionResult<IEnumerable<SpaceBody>> Get(
[FromQuery]
[SomeAttribute]string some,
int minDistance, int maxDistance)
IsValid 方法将通过以下查询调用:
- Api/SpaceBody?some=xxx
但不会被调用:
- Api/SpaceBody
- Api/SpaceBody?some=
直到这里它才有意义:如果查询中没有参数,框架如何知道要应用哪个过滤器?好吧,它应该通过动作方法签名知道
如果我将签名更改为:
public ActionResult<IEnumerable<SpaceBody>> Get(
[FromQuery]
[Required, Some]string some,
int minDistance, int maxDistance)
自定义属性的 IsValid 方法也会触发:
- Api/SpaceBody
- Api/SpaceBody?some=
那么,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”相关的验证?
解决方案
继承自,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;
}
}
推荐阅读
- azure - 用于政府云中 Azure 存储的 TLS
- yaml - Circleci:找不到名为发布最新的作业的定义
- r - 使用 str_detect 过滤多个列
- spring - org.springframework.dao.InvalidDataAccessApiUsageException:分离的实体传递给坚持:
- ansible - Ansible:我怎样才能只恢复标准输出的一部分
- sql-server - 如何通过 Jdbc.getConnection 从 google sheet 连接到 sql server
- typescript - TypeScript 错误:不能使用命名空间“可选”作为类型。TS2709 在尝试为 3rd 方库制作类型文件时
- office365 - Exchange 使用“ErrorOverBudget”代码报告错误
- c++ - 任意精度浮点库的实现
- sql - 使用 WITH 减去两个查询