Dans MVC, lorsque nous publions un modèle sur une action, nous procédons comme suit pour valider le modèle par rapport à l'annotation de données de ce modèle:
if (ModelState.IsValid)
Si nous marquons une propriété comme [Obligatoire], ModelState.IsValid validera cette propriété si contient une valeur ou non.
Ma question: Comment puis-je créer et exécuter manuellement un validateur personnalisé?
P.S. Je parle uniquement du validateur backend.
Dans .NET Core, vous pouvez simplement créer une classe qui hérite de ValidationAttribute
. Vous pouvez voir les détails complets dans ASP.NET Core MVC Docs .
Voici l'exemple tiré directement de la documentation:
public class ClassicMovieAttribute : ValidationAttribute
{
private int _year;
public ClassicMovieAttribute(int Year)
{
_year = Year;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Movie movie = (Movie)validationContext.ObjectInstance;
if (movie.Genre == Genre.Classic && movie.ReleaseDate.Year > _year)
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
J'ai adapté l'exemple pour exclure la validation côté client, comme demandé dans votre question.
Pour utiliser ce nouvel attribut (à nouveau dans la documentation), vous devez l'ajouter au champ correspondant:
[ClassicMovie(1960)]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
Voici un autre exemple plus simple pour s'assurer qu'une valeur est true
:
public class EnforceTrueAttribute : ValidationAttribute
{
public EnforceTrueAttribute()
: base("The {0} field must be true.") { }
public override bool IsValid(object value) =>
value is bool valueAsBool && valueAsBool;
}
Ceci est appliqué de la même manière:
[EnforceTrue]
public bool ThisShouldBeTrue { get; set; }
Pour créer un attribut de validation personnalisé dans .Net Core
, vous devez hériter de IModelValidator
et implémenter la méthode Validate
.
public class ValidUrlAttribute : Attribute, IModelValidator
{
public string ErrorMessage { get; set; }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
var url = context.Model as string;
if (url != null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
return Enumerable.Empty<ModelValidationResult>();
}
return new List<ModelValidationResult>
{
new ModelValidationResult(context.ModalMetadata.PropertyName, ErrorMessage)
};
}
}
public class Product
{
public int ProductId { get; set; }
[Required]
public string ProductName { get; set; }
[Required]
[ValidUrl]
public string ProductThumbnailUrl { get; set; }
}
Cette approche permettra-t-elle de travailler avec la propriété "ModelState.IsValid" dans la méthode d'action du contrôleur?
Oui! L'objet ModalState
reflètera correctement les erreurs.
Cette approche peut-elle être appliquée à la classe de modèle? Ou peut-il être utilisé uniquement avec les propriétés de la classe de modèle?
Je ne sais pas si cela pourrait être appliqué au niveau de la classe. Je sais que vous pouvez obtenir des informations sur la classe auprès de ModalValidationContext
si:
context.Modal
: renvoie la valeur de la propriété à validercontext.Container
: retourne l'objet contenant la propriétécontext.ActionContext
: fournit des données de contexte et décrit la méthode d'action qui traite la demande.context.ModelMetadata
: décrit la classe de modèle en cours de validationCet attribut de validation ne fonctionne pas avec la validation du client, comme demandé dans l'OP.
J'ai utilisé la même solution pour check NULL mais cela ne fonctionne pas pour moi.
using System.ComponentModel.DataAnnotations;
/// <summary>
/// Defines the <see cref="NullCheckAttribute" />
/// </summary>
public class NullCheckAttribute : ValidationAttribute
{
public NullCheckAttribute()
: base("The {0} field should not be null.") { }
public override bool IsValid(object value)
{
return value != null;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
return new ValidationResult("The value should not be null");
}
return ValidationResult.Success;
}
}