Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:-

public async Task<IActionResult> Dashboard(GroupBy groupBy)

My problem is when i am passing correct enum it will give output. But if I pass any invalid enum it will throw error which is built-in error of ASP.NET Core. I tried to implement but while calling this api it won't go inside my custom validator. When I am passing valid enum it will go inside my validator.

So, I want to implement custom validation for it. Somebody please help

like image 827
Shashwat Prakash Avatar asked Mar 14 '26 23:03

Shashwat Prakash


1 Answers

When the value is incorrect, the model binder can't create the GroupBy instance and can't call custom validation on this instance.

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. 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 :

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. More detail on the official documentation.

like image 181
vernou Avatar answered Mar 16 '26 13:03

vernou



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!