Search code examples
c#asp.netvalidationasp.net-coreasp.net-core-webapi

How can I validate multiple action parameters with ValidationAttribute in ASP.NET Core 3.0


Consider the following controller action signatures:

[HttpGet]
public IEnumerable<Whatever> Get(DateTime from, DateTime to)

Or

[HttpGet]
public Whatever Get(int amount, SomeUnit unit)

I'd like to be able to run a validation on parameters supplied by the caller. In both cases what passes as valid input in the first parameter depends on the value of second one.

So far I have failed to find an explanation or example of how this can be achieved with validation attributes in ASP.NET Core 3.0

I see that for example CustomValidationAttribute allows using it on methods. I can give it a method that IsValid will delegate to and provide object and validation context:

IsValid(Object, ValidationContext)

I checked documentation of both IsValid and ValidationContext and I cannot find any hint on how would I access the parameters passed to the validated action.

While googling for hints I found an excerpt from "Pro ASP.NET Web API" HTTP Web Services in ASP.NET" where they implement something similar. The excerpt ends however before the code is shown and I don't have that book (didn't find a corresponding book but on ASP.NET Core).


Solution

  • CustomValidationAttribute is used for properties and parameters. For an action validation you should write your own filter. Here is an example:

    public class MyValidationAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if(context.ModelState.IsValid)
            {
                var dateFrom = (DateTime)context.ActionArguments["from"];
                var dateTo = (DateTime)context.ActionArguments["to"];
    
                if(dateFrom <= dateTo)
                {
                    // continue the flow in pipeline
                    return;
                }
            }
    
            context.Result = new BadRequestResult();
        }
    }
    

    And then you can use it in your controller:

    [HttpGet]
    [MyValidation]
    public IEnumerable<Whatever> Get(DateTime from, DateTime to)
    {
        // Here ModelState.IsValid is true
    }