当前位置: 首页 > 知识库问答 >
问题:

自定义验证属性的依赖注入

文建业
2023-03-14

我创建了一个自定义验证属性,我想将其用于 API 控制器 DTO。此属性需要来自已配置选项的值,这就是我在构造函数中注入这些值的原因,以便我以后可以在 IsValidFormatErrorMessage 方法中使用选项服务。

internal class MyValidationAttribute : ValidationAttribute
{
    private readonly IOptionsMonitor<MyOptions> myOptionsMonitor;

    public MyValidationAttribute(IOptionsMonitor<MyOptions> myOptionsMonitor)
    {
        this.myOptionsMonitor = myOptionsMonitor;
    }

    public override bool IsValid(object value)
    {
        // ... use myOptionsMonitor here ...

        return false;
    }

    public override string FormatErrorMessage(string name)
    {
        // ... use myOptionsMonitor here ...

        return string.Empty;
    }
}

不幸的是,当我想在DTO中将其用作属性时

internal class MyDTO
{
    [MyValidationAttribute]
    public string Foo { get; set; }
}

我收到错误信息

没有给出对应于'MyValidationAttribute.MyValidationAttribute(IOptionsMonitor)'所需形式参数'myOptionsMonitor'的参数

有没有一种方法可以将依赖注入用于验证属性?我知道我可以像这样使用< code>ValidationContext

internal class MyValidationAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value != null)
        {
            IOptionsMonitor<MyOptions> myOptionsMonitor = validationContext.GetService<IOptionsMonitor<MyOptions>>();

            // ...

            return ValidationResult.Success;
        }

        return new ValidationResult("Something failed");
    }
}

但是我想使用基类中的FormatErrorMessage方法,并且无法访问选项服务。

我目前的解决方案

目前,这是我正在使用的代码

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
internal class CustomValidationAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        IOptionsMonitor<MyOptions> myOptionsMonitor = validationContext.GetService<IOptionsMonitor<MyOptions>>();
        Dictionary<string, string> myMap = myOptionsMonitor.CurrentValue.MyMap;
        string key = value.ToString() ?? string.Empty;

        if (myMap.ContainsKey(key))
            return ValidationResult.Success;

        string[] formattedKeys = myMap.Keys.Select(key => $"'{key}'").ToArray();
        string keysText = string.Join(" / ", formattedKeys);
        string errorMessage = $"Invalid value. Valid ones are {keysText}";

        return new ValidationResult(errorMessage);
    }
}

共有1个答案

公羊灿
2023-03-14

属性不是为此目的而设计的。但是您可以使用动作过滤器。

让我们使你的属性尽可能简单,我们不需要任何验证逻辑。

[AttributeUsage(AttributeTargets.Property)]
public class CustomValidationAttribute : Attribute
{ }

对于我的例子,我创建了我们要注入的服务

public class SomeService
{
    public bool IsValid(string str)
    {
        return str == "Valid";
    }
}

还有一个我们要验证的类

public class ClassToValidate
{
    [CustomValidation]
    public string ValidStr { get; set; } = "Valid";
    
    [CustomValidation]
    public string InvalidStr { get; set; } = "Invalid";
}

现在我们终于可以创建操作过滤器来验证我们的属性了。在下面的片段中,我们连接到ASP.NETCore管道以在控制器操作执行之前执行代码。在这里,我获取操作参数并尝试在任何属性上查找CustomValidation属性。如果它在那里,从属性中获取值,强制转换为类型(我只是调用. ToString())并传递给您的服务。根据从服务返回的值,我们继续执行或向ModelState字典添加错误。

public class CustomValidationActionFilter : ActionFilterAttribute
{
    private readonly SomeService someService;

    public CustomValidationActionFilter(SomeService someService)
    {
        this.someService = someService;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var actionArguments = context.ActionArguments;

        foreach (var actionArgument in actionArguments)
        {
            var propertiesWithAttributes = actionArgument.Value
                .GetType()
                .GetProperties()
                .Where(x => x.GetCustomAttributes(true).Any(y => y.GetType() == typeof(CustomValidationAttribute)))
                .ToList();

            foreach (var property in propertiesWithAttributes)
            {
                var value = property.GetValue(actionArgument.Value).ToString();

                if (someService.IsValid(value))
                    continue;
                else
                    context.ModelState.AddModelError(property.Name, "ModelState is invalid!!!");
            }
        }

        base.OnActionExecuting(context);
    }
}

不要忘记在“启动.cs”中将筛选器添加到管道中!

services.AddMvc(x =>
{
    x.Filters.Add(typeof(CustomValidationActionFilter));
});

更新:

如果您严格希望在属性内部使用依赖注入,可以使用服务定位器反模式。为此,我们需要模拟<code>DependencyResolver。当前来自ASP。NET MVC

public class CustomValidationAttribute : ValidationAttribute
{
    private IServiceProvider serviceProvider;

    public CustomValidationAttribute()
    {
        serviceProvider = AppDependencyResolver.Current.GetService<IServiceProvider>();
    }

    public override bool IsValid(object value)
    {
        // scope is required for scoped services
        using (var scope = serviceProvider.CreateScope())
        {
            var service = scope.ServiceProvider.GetService<SomeService>();

            return base.IsValid(value);
        }
    }
}


public class AppDependencyResolver
{
    private static AppDependencyResolver _resolver;

    public static AppDependencyResolver Current
    {
        get
        {
            if (_resolver == null)
                throw new Exception("AppDependencyResolver not initialized. You should initialize it in Startup class");
            return _resolver;
        }
    }

    public static void Init(IServiceProvider services)
    {
        _resolver = new AppDependencyResolver(services);
    }

    private readonly IServiceProvider _serviceProvider;

    public object GetService(Type serviceType)
    {
        return _serviceProvider.GetService(serviceType);
    }

    public T GetService<T>()
    {
        return (T)_serviceProvider.GetService(typeof(T));
    }

    private AppDependencyResolver(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }
}

它应该在<code>Startup.cs<code>中初始化

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    AppDependencyResolver.Init(app.ApplicationServices);

    // other code
}
 类似资料:
  • 我试图用Hibernate Validation 6.0.1定义约束定义,其中验证器位于相对于约束注释的不同位置(.jar/项目)。Aka,我有我想要验证的对象,它们位于带有注释定义的项目“api”中,但我将在项目“modules/common”中有验证器 我遵循文档中的描述。 配置文件 约束注释 验证器 我的问题我的问题是,如果我没有将“@约束(validatedby={})”放在注释中,我会得

  • 在MVC中,我可以创建一个模型验证器,它可以接受依赖关系。我通常使用FluentValidation来实现这一点。例如,这允许我在帐户注册时检查电子邮件地址是否未被使用(注意:这是一个简化的示例): Web API与FluentValidation不存在这样的集成。在这方面已经进行了几次尝试,但都没有解决依赖注入方面,并且只使用静态验证器。 这是由于MVC和Web API在ModelValidat

  • 我正在使用Hibernate@NotNull验证器,我正在尝试创建自定义消息来告诉用户哪个字段在空时产生了错误。类似这样的东西: (这将在我的ValidationMessages.properties文件中)。 其中,{0}应该是通过以下方式传递给验证程序的字段名: 我有办法做到吗?

  • 那里是自定义验证器: 当我调用时,我在插入的的上得到了一个。 我假设Spring会正确地注入定制验证器,我必须告诉他。但是,我不知道怎么做。我已经尝试了一些通过堆栈溢出和谷歌找到的东西,但没有运气。 任何帮助都将不胜感激。

  • 我正在开发spring mvc应用程序,我应该在spring mvc validator的基础上应用验证。第一步,我为类和设置控制器添加了注释,效果很好。现在我需要实现自定义验证器来执行复杂的逻辑,但我想使用现有的注释并添加额外的检查。 我的用户类: 我的验证器: 我的控制器: 那么,有可能同时使用自定义验证器和注释吗?如果是,怎么做?

  • 我正在使用JavaEE6,需要从“.properties”文件加载配置。是否有推荐的方法(最佳实践)来加载这些值​​从配置文件中使用依赖项注入?我在Spring中找到了这方面的注释,但还没有找到JavaEE的“标准”注释。 这家伙从零开始开发了一个解决方案: http://weblogs.java.net/blog/jjviana/archive/2010/05/18/applicaction-c