简体   繁体   English

ASP.NET 内核中枚举的自定义验证器

[英]Custom Validator for Enum in ASP.NET Core

public enum GroupBy
{
    status = 0,
    dueDate = 1,
    requester = 2,
    assignee = 3
}

I am using this enum in params of web api like this:-我在 web api 的参数中使用这个枚举,如下所示:-

public async Task<IActionResult> Dashboard(GroupBy groupBy)

My problem is when i am passing correct enum it will give output.我的问题是当我传递正确的枚举时,它会给出 output。 But if I pass any invalid enum it will throw error which is built-in error of ASP.NET Core.但是如果我传递任何无效的枚举,它会抛出错误,这是 ASP.NET 核心的内置错误。 I tried to implement but while calling this api it won't go inside my custom validator.我试图实现,但是在调用这个 api 时,它不会在我的自定义验证器中实现 go。 When I am passing valid enum it will go inside my validator.当我通过有效枚举时,它会在我的验证器中 go 。

So, I want to implement custom validation for it.所以,我想为它实现自定义验证。 Somebody please help有人请帮忙

When the value is incorrect, the model binder can't create the GroupBy instance and can't call custom validation on this instance.当值不正确时,model binder 无法创建 GroupBy 实例,也无法在此实例上调用自定义验证。

A solution is to change the input parameter type to string and do manually the check and parse step:一种解决方案是将输入参数类型更改为字符串并手动执行检查和解析步骤:

public async Task<IActionResult> Dashboard(string groupBy)
{
    if(!Enum.TryParse(groupBy, out GroupBy by))
    {
        ModelState.AddModelError(nameof(groupBy), $"The value is invalid. Valid value : {Enum.GetValues(typeof(GroupBy))}");
        return BadRequest(ModelState);
    }
    return Ok(by);
}

Other solution is to override the model binder behavior .其他解决方案是覆盖model binder behavior For this, you need create a custom binder:为此,您需要创建一个自定义活页夹:

public class GroupByBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        // Try to fetch the value of the argument by name
        var modelName = "groupBy";
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

        var value = valueProviderResult.FirstValue;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        // Custom validation
        if (!Enum.TryParse(value, out GroupBy groupBy))
        {
            bindingContext.ModelState.AddModelError(modelName, $"The value is invalid. Valid value : {Enum.GetValues(typeof(GroupBy))}");
            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(groupBy);
        return Task.CompletedTask;
    }
}

And now you can:现在您可以:

public async Task<IActionResult> Dashboard2([ModelBinder(typeof(GroupByBinder))] GroupBy groupBy)
{
    if(!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    return Ok(groupBy);
}

You can override this behavior to all GroupBy input parameter:您可以将此行为覆盖到所有 GroupBy 输入参数:

public class GroupByBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(GroupBy))
        {
            return new BinderTypeModelBinder(typeof(GroupByBinder));
        }

        return null;
    }
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers(options =>
        {
            options.ModelBinderProviders.Insert(0, new GroupByBinderProvider());
        });
    }
}

Warning: Model Builder isn't used when the data come from JSON or XML content.警告:当数据来自 JSON 或 XML 内容时,不使用 Model Builder。 More detail on the official documentation .更详细的官方文档

I am not entirely sure if I understand your problem correctly but I think what you are looking for is model validation.我不完全确定我是否正确理解了您的问题,但我认为您正在寻找的是 model 验证。 Here is a more generic approach than the already provided answer:这是比已经提供的答案更通用的方法:

  1. Custom validation attribute:自定义验证属性:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class DefinedEnumValueAttribute : ValidationAttribute
{
    private readonly Type enumType;

    public DefinedEnumValueAttribute(Type enumType)
    {
        if (!enumType.IsEnum)
        {
            throw new ArgumentException($"The given type is not an enum.");
        }

        this.enumType = enumType;
    }

    public override bool IsValid(object value)
    {
        if (value is IEnumerable enumerable)
        {
            return enumerable.Cast<object>().All(val => Enum.IsDefined(enumType, val));
        }
        else
        {
            return Enum.IsDefined(enumType, value);
        }
    }
}
  1. Change your endpoint to something like the following:将您的端点更改为以下内容:
public class Settings 
{
    [DefinedEnumValue(typeof(GroupBy))]
    public GroupBy GroupBy { get; set; }
}

public async Task<IActionResult> Dashboard(Settings settings)
{
    if(!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    
    // do your thing

    return Ok();
}

Please note that the attribute can be used for any enum and also for arrays and other enumerables:请注意,该属性可用于任何枚举,也可用于 arrays 和其他枚举:

public class Settings 
{
    [DefinedEnumValue(typeof(GroupBy))]
    public GroupBy[] Groupings { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM