Data Model Validation
Closed this issue · 1 comments
foconnor-DS commented
Looks like the Model's don't participate in the Data Validation system, when we custom Model Bind.
I added some code so that Data Model Validation would work:
public class JsonModelBinder : IModelBinder
{
private readonly MvcJsonOptions _options;
public JsonModelBinder(IOptions<MvcJsonOptions> options) =>
_options = options.Value;
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
// Test if a value is received
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != ValueProviderResult.None)
{
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
// Deserialize from string
string serialized = valueProviderResult.FirstValue;
// Use custom json options defined in startup if available
object deserialized = _options?.SerializerSettings == null ?
JsonConvert.DeserializeObject(serialized, bindingContext.ModelType) :
JsonConvert.DeserializeObject(serialized, bindingContext.ModelType, _options.SerializerSettings);
// DataAnnotation Validation. Validate Properties and Fields.
var validationResultProps = from prop in TypeDescriptor.GetProperties(deserialized).Cast<PropertyDescriptor>()
from attribute in prop.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(prop.GetValue(deserialized))
select new { Propertie = prop.Name, ErrorMessage = attribute.FormatErrorMessage(string.Empty) };
var validationResultFields = from field in TypeDescriptor.GetReflectionType(deserialized).GetFields().Cast<FieldInfo>()
from attribute in field.GetCustomAttributes<ValidationAttribute>()
where !attribute.IsValid(field.GetValue(deserialized))
select new { Propertie = field.Name, ErrorMessage = attribute.FormatErrorMessage(string.Empty) };
var errors = validationResultFields.Concat(validationResultFields);
// Add the ValidationResult's to the ModelState
foreach (var validationResultItem in errors)
bindingContext.ModelState.AddModelError(validationResultItem.Propertie, validationResultItem.ErrorMessage);
// Set successful binding result
bindingContext.Result = ModelBindingResult.Success(deserialized);
#if NET451
return Task.FromResult(0);
#else
return Task.CompletedTask;
#endif
}
#if NET451
return Task.FromResult(0);
#else
return Task.CompletedTask;
#endif
}
}
I hacked this together from various internet posts. I wonder if there isn't an easier way to just ask the deserialzied object to validate itself?
For now, I iterate the Properties and Fields, and call the ValidationAttribute.IsValid against the data.