I am currently trying to write a Web API application where one of the parameters I'd like to validate is a query parameter (that is, I wish to pass it in in the form /route?offset=0&limit=100
):
[HttpGet]
public async Task<HttpResponseMessage> GetItems(
int offset = 0,
int limit = 100)
{
if (!ModelState.IsValid)
{
// Handle error
}
// Handle request
}
In particular, I want to ensure that "offset" is greater than 0, since a negative number will cause the database to throw an exception.
I went straight for the logical approach of attaching a ValidationAttribute
to it:
[HttpGet]
public async Task<HttpResponseMessage> GetItems(
[Range(0, int.MaxValue)] int offset = 0,
int limit = 100)
{
if (!ModelState.IsValid)
{
// Handle error
}
// Handle request
}
This does not cause any errors at all.
After a lot of painful debugging into ASP.NET, it appears to me that this may be simply impossible. In particular, because the offset
parameter is a method parameter rather than a field, the ModelMetadata
is created using GetMetadataForType
rather than GetMetadataForProperty
, which means that the PropertyName
will be null
. In turn, this means that AssociatedValidatorProvider
calls GetValidatorsForType
, which uses an empty list of attributes even though the parameter had attributes on it.
I don't even see a way to write a custom ModelValidatorProvider
in such a way as to get at that information, because the information that this was a function parameter seems to have been lost long ago. One way to do that might be to derive from the ModelMetadata
class and use a custom ModelMetadataProvider
as well but there's basically no documentation for any of this code so it would be a crapshoot that it actually works correctly, and I'd have to duplicate all of the DataAnnotationsModelValidatorProvider
logic.
Can someone prove me wrong? Can someone show me how to get validation to work on a parameter, similar to how the BindAttribute
works in MVC? Or is there an alternative way to bind query parameters that will allow the validation to work correctly?
int offset
andint limit
and add the validation attributes to the model property. Then change the method parameter to be your model -public async Task<HttpResponseMessage> GetItems(yourModel model)
– Stylish?offset=10&limit=10
. If you have a way, please answer the question. – Delilahdelimitoffset
andlimit
need to be properties (with{get; set; }
) and if you want defaults, then use a parameterless constructor – Stylish[FromUri]
. For more information, see link – Alforja