In my previous post I described how to solve “The value ‘NaN’ is not valid.” when an int parameter contains NaN. My colleauge then convinced me to solve this with a custom ModelBinder instead. So, here is the solution.
First we create a custom modelbinder.
public class IntNanModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (string.IsNullOrWhiteSpace(valueProviderResult.FirstValue) || valueProviderResult.FirstValue == "NaN")
{
bindingContext.Result = ModelBindingResult.Failed();
} else if (Int32.TryParse(valueProviderResult.FirstValue, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var intValue))
{
bindingContext.Result = ModelBindingResult.Success(intValue);
} else
{
throw new Exception($"Parameter '{bindingContext.ModelName}' with value '{valueProviderResult.FirstValue}' is not an int.");
}
return Task.CompletedTask;
}
}
This can now be used directly in your endpoint by
[HttpGet]
[Route("list")]
public IActionResult List([ModelBinder(BinderType = typeof(IntNanModelBinder))] int skip = 0, int take = 20)
{
// Code
}
But we want to use this on all our endpoints where we have int as parameters. So we need to create a ModelBinderProvider and add it in our startup.
public class IntNanModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(int))
{
return new BinderTypeModelBinder(typeof(IntNanModelBinder));
}
return null;
}
}
And in ConfigureServices in Startup.cs
services.AddMvc(c =>
{
c.ModelBinderProviders.Insert(0, new IntNanModelBinderProvider());
});
And happy days, it works magically.