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

Custom Validator for Enum in ASP.NET Core

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

我在 web api 的参数中使用这个枚举是这样的:-

public async Task<IActionResult> Dashboard(GroupBy groupBy)

我的问题是当我传递正确的枚举时它会给出输出。但是如果我传递任何无效的枚举,它会抛出 built-in ASP.NET Core 的错误。我试图实现,但在调用此 api 时,它不会进入我的自定义验证器。当我传递有效枚举时,它将进入我的验证器。

所以,我想为它实现自定义验证。有人请帮忙

当值不正确时,模型绑定器无法创建 GroupBy 实例,也无法对此实例调用自定义验证。

一个解决方案是将输入参数类型更改为字符串并手动执行检查和解析步骤:

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);
}

其他解决方案是覆盖 the model binder behavior。 为此,您需要创建一个自定义活页夹:

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;
    }
}

现在您可以:

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

您可以将此行为覆盖到所有 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());
        });
    }
}

警告:当数据来自 JSON 或 XML 内容时,不使用模型生成器。有关 the official documentation.

的更多详细信息

我不完全确定我是否正确理解你的问题,但我认为你正在寻找的是模型验证。这是比已经提供的答案更通用的方法:

  1. 自定义验证属性:
[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. 将您的端点更改为如下内容:
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();
}

请注意,该属性可用于任何枚举,也可用于数组和其他可枚举:

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