I am trying to build a custom model binder inheriting from DefaultModelBinder
. In this implementation, I want to access the validation rules metadata (from FluentValidation), as they contain information I need to override the ModelState
error messages.
Note that the answer in that link won't help me, as I'm not using resources for validation messages. The messages are stored in FluentValidation rules. The reason normal validation doesn't work, similar to the issue in that link, is because .NET's model binders take over and set the message as well as nulling out the value, thus making the value valid, as far as the FluentValidation rules are concerned.
I can get this to work, if only I could access my model's validation rules, per property and rule.
Here is an example model:
[Validator(typeof(SampleValidator))]
public SampleModel
{
public DateTime SomeDate { get; set; }
public int SomeInt { get; set; }
public decimal SomeDecimal { get; set; }
}
FluentValidation, out of the box, doesn't provide validation for data types, so I have built my own client-side validators to override the validation messages (via Fluent adapters). My problem is just with the server-side validation, which is taken over by .NET's default model binder, as mentioned in that linked discussion.
I'm trying to do this from the DefaultModelBinder.SetProperty override:
protected override void SetProperty(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
System.ComponentModel.PropertyDescriptor propertyDescriptor,
object value)
{
}
Here is a sample validator. THe server-side validation IsValid
always returns true bacuase, as I said above, the DefaultModelBinder
always overrides the value to null when a non-date is entered:
public class DateValidator : PropertyValidator, IDateValidator
{
public DateValidator()
: base(() => "{PropertyName} must be a date.")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
return true;
}
}
The validation adapter just wires up the validator to the client-side adapter and parameters:
public class DateFluentValidationPropertyValidator : FluentValidationPropertyValidator
{
private IDateValidator DateValidator
{
get { return (IDateValidator)Validator; }
}
public DateFluentValidationPropertyValidator(ModelMetadata metadata, ControllerContext controllerContext, PropertyRule rule, IPropertyValidator validator)
: base(metadata, controllerContext, rule, validator)
{
ShouldValidate = false;
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
if (!ShouldGenerateClientSideRules()) yield break;
var formatter = new MessageFormatter().AppendPropertyName(Rule.GetDisplayName());
string message = formatter.BuildMessage(DateValidator.ErrorMessageSource.GetString());
yield return new ModelClientValidationRule
{
ValidationType = "date",
ErrorMessage = message
};
}
}
And here is how I would wire a property up to the rule:
public class SampleValidator : AbstractValidator<SampleModel>
{
public SampleValidator()
{
this.RuleFor(m => m.SomeDate).Date();
}
}
How can I access the validation rules & metadata for my model with these values?