2010-09-19 9 views
15

donc selon GuIValidatableObject.Validate() devrait obtenir appelé lorsqu'un contrôleur valide son modèle (avant ModelState.IsValid) mais tout simplement faire le modèle en œuvre IValidatableObject ne semble pas fonctionner, parce que Validate(..) ne soit pas appelé . Est-ce que quelqu'un sait s'il y a quelque chose d'autre que je dois câbler pour que cela fonctionne?ModelState.IsValid vs IValidateableObject dans MVC3

EDIT:

Voici le code tel que demandé.

public class LoginModel : IValidatableObject 
{ 
    [Required] 
    [Description("Email Address")] 
    public string Email { get; set; } 

    [Required] 
    [Description("Password")] 
    [DataType(DataType.Password)] 
    public string Password { get; set; } 

    [DisplayName("Remember Me")] 
    public bool RememberMe { get; set; } 

    public int UserPk { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     var result = DataContext.Fetch(db => { 

      var user = db.Users.FirstOrDefault(u => u.Email == Email); 

      if (user == null) return new ValidationResult("That email address doesn't exist."); 
      if (user.Password != User.CreateHash(Password, user.Salt)) return new ValidationResult("The password supplied is incorrect."); 

      UserPk = user.UserPk; 
      return null; 
     }); 

     return new List<ValidationResult>(){ result }; 
    } 
} 

L'action. (Je ne fais rien de spécial dans le contrôleur ...)

[HttpPost] 
public ActionResult Login(LoginModel model) 
{ 
    if (ModelState.IsValid) 
    { 
     FormsAuthentication.SetAuthCookie(model.Email, model.RememberMe); 
     return Redirect(Request.UrlReferrer.AbsolutePath); 
    } 

    if (ControllerContext.IsChildAction || Request.IsAjaxRequest()) 
     return View("LoginForm", model); 

    return View(model); 
} 

Je mets un point d'arrêt sur la première ligne de LoginModel.Validate() et il ne semble pas être touché.

+0

Vous êtes le code semble très bien. Exactement comme il se doit. Juste un point d'intérêt, mais avez-vous un modèle en double? Je sais que j'ai un modèle de vue et un modèle db pour chaque objet. Votre contrôleur pourrait-il référencer le mauvais modèle? – Buildstarted

+2

Aussi, en note: vous ne devriez certainement retourner qu'une seule erreur si le nom d'utilisateur ou mot de passe est invalide et pas des erreurs distinctes. C'est simplement pour la sécurité que je peux tester chaque champ individuellement pour trouver un nom d'utilisateur et ensuite travailler sur le mot de passe pour cet utilisateur. Ce n'est pas obligatoire mais c'est une bonne idée :) – Buildstarted

+0

Vous pouvez utiliser 'return return DataContext ...' plutôt que de retourner une nouvelle liste. Ce serait plus joli et plus rapide. – pipedreambomb

Répondre

18

Il n'y a rien de plus à faire que de l'ajouter au modèle que vous validez. Voici un exemple de validation

public class User : IValidatableObject { 
    public Int32 UserID { get; set; } 
    public string Name { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { 
     //do your validation 

     return new List<ValidationResult>(); 
    } 
} 

Et votre contrôleur utiliserait ce modèle

public ActionResult Edit(User user) { 
    if (ModelState.IsValid) { 
    } 
} 

Hope this helps. Les autres conditions sont .net 4 et les annotations de données - dont vous avez évidemment besoin jsut pour ivalidatableobject. Postez des problèmes et nous verrons si nous ne pouvons pas les résoudre - comme poster votre modèle et votre contrôleur ... vous pourriez manquer quelque chose.

+23

Vous avez raison, le seul inconvénient est que si vous avez des attributs de validation qui entraînent l'invalidité du modèle, Validate n'est jamais appelé. C'était mon problème. –

+0

Ah, c'est intéressant. Merci pour ça. – Buildstarted

+2

Cette mise en garde me frappe comme une chose très pratique à savoir :) +1 aux deux –

6

Validation à l'aide de DefaultModelBinder est un processus en deux étapes. Tout d'abord, Data Annotations sont validés. Ensuite (et seulement si la validation des annotations de données a abouti à zéro erreur), IValidatableObject.Validate() est appelée. Tout se passe automatiquement lorsque votre action de publication a un paramètre viewmodel. ModelState.IsValid ne fait rien en tant que tel. Il indique simplement si un élément de la collection ModelState possède un code ModelErrorCollection non vide.

+0

Je me suis toujours posé des questions à ce sujet. Indique si la validation a été appelée automagiquement dans DefaultModelBinder ou si ModelState.IsValid a vérifié une propriété cachée '_hadBeenValidated' et a appelé validate si' _hasBeenValidated == false'. –

+2

Le comportement décrit n'est pas exactement le vrai: s'il vous plaît voir http://stackoverflow.com/questions/8153602/ivalidatableobject-validate-combined-with-dataannotations – Diego