Reputation: 2573
I'm attempting to validate the input of a text box which corresponds to a property of type double
in my model. If the user inputs "foo"
I want to know about it so I can display an error. However, the ValueProvider
is dropping the value silently (no errors are added to the ModelState
).
In a normal submission, I fill in "2"
for the text box corresponding to myDouble
and submit the form.
Inspecting controllerContext.HttpContext.Request.Form
shows that myDouble=2
, among other correct inputs. bindingContext.ValueProvider.GetValue("myDouble") == 2
, as expected. The bindingContext.ModelState.Count == 6
and bindingContext.ModelState["myDouble"].Errors.Count == 0
. Everything is good and the model binds as expected.
Then I fill in "foo" for the text box corresponding to myDouble
and submitted the form.
Inspecting controllerContext.HttpContext.Request.Form
shows that myDouble=foo
, which is what I expected. However, bindingContext.ValueProvider.GetValue("myDouble") == null
and bindingContext.ModelState.Count == 5
(The exact number isn't important, but it's one less than before). Looking at the ValueProvider
, is as if myDouble
was never submitted and the model binding occurs as if it wasn't. This makes it difficult to differentiate between a bad input and no input.
Is this the expected behavior of ValueProvider
? Is there a way to get ValueProvider
to report when conversion fails without implementing a custom ValueProvider
? Thanks!
Upvotes: 1
Views: 705
Reputation: 378
You can implement custom model binding logic using by implementing IModelBinder. This will put the data validation logic at the model binding level - thus being usable for any type of ValueProvider. In your situation, the model binder would determine that when myDouble = "foo" is not a double and add an exception to the ModelState errors showing the invalid value.
public class CustomDoubleBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}
decimal tempDouble = 0m;
if (bindingContext.ValueProvider.GetValue(bindingContext.ModelName) != null)
{
if (double.TryParse(bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue, out tempDecimal))
{
bindingContext.ModelState[bindingContext.ModelName].Errors.Add("Error parsing double value: " + bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue);
}
}
return tempDouble;
}
}
Having created this custom model binder, you will then need to register it in the Global.asax:
protected void Application_Start()
{
ModelBinders.Binders[typeof(double)] = new CustomDoubleBinder();
}
Upvotes: 0
Reputation: 93424
Part of the problem here is that your model has a type of double
.
The problem is that double
cannot be null, and as such will default to a value of 0, thus on submit.. if the ValueProvider
returns null, the value of the field will still be 0 and validation will pass.
You should make the double
nullable, by using double?
and then add a Required attribute to the property. If the type is not required, then you can add a regular expression validator.
Upvotes: 1