12

I'd like to validate an input on a Web API REST command. I'd like it to work something like State below being decorated with an attribute that limits the valid values for the parameter.

public class Item {
    ...

    // I want State to only be one of "New", "Used", or "Unknown"
    [Required]
    [ValidValues({"New", "Used", "Unknown"})]
    public string State { get; set; }

    [Required]
    public string Description { get; set; }

    ...
}

Is there a way to do this without going against the grain of Web API. Ideally the approach would be similar to Ruby on Rails' custom validation.

4 Answers 4

30

Create a custom validation attribute derived from ValidationAttribute and override the IsValid member function.

public class ValidValuesAttribute: ValidationAttribute
{
  string[] _args;

  public ValidValuesAttribute(params string[] args)
  {
    _args = args;
  }

  protected override ValidationResult IsValid(object value, ValidationContext validationContext)
  {
    if (_args.Contains((string)value))
      return ValidationResult.Success;
    return new ValidationResult("Invalid value.");
  }
}

Then you can do

[ValidValues("New", "Used", "Unknown")]

The above code has not been compiled or tested.

Sign up to request clarification or add additional context in comments.

5 Comments

Pretty good for not having compiled or testing. This worked with minimal tweaking: The constructor didn't like the params before the args parameter. Also, the attribute looks like this [ValidValues(new[] {"New", "Used", "Unknown"})]
I compiled the code above. I had to make the constructor public. Aside from that, it worked as expected. For me, the params worked.
What if I don't want to pass it a hardcoded list? For example, a reference to an existing list. Any ideas for that which still use dependency injection versus something like a Singleton or ServiceLocator? I suppose that isn't possible because Attributes can impact compile time.
Is this approach reflected in the Swagger documentation? - in a similar way that an enum would be? (here's your available options)
@RobMcCabe I don't know the answer to that, but if I was to guess, I'd say no, not without more work to make it so.
2

you can also do this using a regular expression as below:

[Required]
[RegularExpression("New|Used|Unknown", ErrorMessage = "Invalid State")]
 public string State{ get; set; }

More details can by found here

Comments

1

How is the value passed to the API? Is it a query param or is it in the body? I'd generally just do a check at the request handler level. If you put accepted values in a List or array you can just used the Contains extension method.

  if (validStates.Contains(input))
  {
     return MethodThatProcessesRequest(requiredData);
  }
  else
  {
     return ErrorHandlingMethod(requiredData);
  }

This type of validation should be done servers side. Feel free to restrict the input on the UI but if you're making a REST API it should validate all input regardless of what your client is doing.

Comments

1

Implement IValidatableObject for the Item which would require you to implement Validate method, then in the Validate write your condition to check whether it is valid, something like:

public IEnumerable<ValidationResult> Validate(ValidationContext context) {
    if (!States.contains(this.State)){
        yield return new ValidationResult("Invalid state.", new[] { "State" });
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.