asp.net-mvc - side - mvc required field validation
Atributos de validaciĆ³n personalizados MVC 2 vs MVC 3 utilizando DataAnnotationsModelValidatorProvider.RegisterAdapter (2)
Lo leí en una publicación, pero ahora no puedo encontrarlo que en MVC 3 no era realmente necesario para crear un Validator, solo el Atributo. ¿Es esto cierto? Sí digo que me resulta confuso que el atributo tenga IClientValidatable en él. Entonces, ¿qué hace la clase DataAnnotationsModelValidator si la anotación tiene el nombre del script del lado del cliente (IClientValidatable) y la capacidad de validar (ValidationAttribute IsValid)?
Sería realmente bueno si no tuviera que registrar el Atributo con el Validador en el global. Se puede hacer esto? ¿Leí algunos consejos malos?
EDITAR: Curiosamente, acabo de probarlo excluyendo el validador, puse toda la lógica en IsValid y funciona muy bien. Supongo que lo único que podría faltar sería el contexto del controlador, pero no estoy seguro de que sea útil en la validación. IsValid tiene ValidationContext que tiene ServiceContainer si necesito un servicio. ¿Alguna desventaja real que no estoy recogiendo aquí?
EDIT 2: Comenzaré con un validador de este ejemplo: http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx
El atributo:
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule modelClientValidationRule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.DisplayName),
ValidationType = "requiredifattribute"
};
modelClientValidationRule.ValidationParameters.Add("requiredifattribute", DependentProperty);
yield return modelClientValidationRule;
}
}
El Validador:
public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
return base.GetClientValidationRules();
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
if (field != null)
{
var value = field.GetValue(container, null);
if ((value == null && Attribute.TargetValue == null) ||
(value.Equals(Attribute.TargetValue)))
{
if (!Attribute.IsValid(Metadata.Model))
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
Con el código actual anterior, necesito registrarme en el archivo Global.asax.cs:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredIfValidator));
Pero si muevo todo en solo el atributo, no tengo que registrarlo:
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectInstance.GetType().GetProperty(DependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && TargetValue == null) ||
(dependentValue.Equals(TargetValue)))
{
if (!innerAttribute.IsValid(value))
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule modelClientValidationRule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.DisplayName),
ValidationType = "requiredifattribute"
};
modelClientValidationRule.ValidationParameters.Add("requiredifattribute", DependentProperty);
yield return modelClientValidationRule;
}
}
¿Hay algún problema con el último bit de código reemplazando a todos los demás? ¿Por qué debería mantener la clase de validador?
CrazyDart,
La interfaz IClientValidatable
se agregó en MVC3.
Su segundo ejemplo muestra un uso válido de esta nueva interfaz. Tiene razón en que no tiene que estar registrado, y proporcionará las reglas necesarias del lado del cliente para la validación, además de realizar la validación necesaria del lado del servidor.
Adelante, disfrútalo.
counsellorben
Cuando uso la última opción de CrazyDart, la parte del servidor funciona muy bien en MVC4.
Excepto que no puedo hacer que la validación del lado del cliente funcione. Nunca busca un campo obligatorio en el lado del cliente (aunque hay algunas etiquetas agregadas).
También he comprobado la segunda publicación del blog (la primera es la inspiración del póster) de Simon Ince sobre esto: http://blogs.msdn.com/b/simonince/archive/2011/02/04/condition-validation-in -asp-net-mvc-3.aspx