2010-09-19 15 views
15

a tal fine i GuIValidatableObject.Validate() dovrebbe ottenere chiamato quando un controller di convalida è il modello (cioè prima ModelState.IsValid) tuttavia semplicemente facendo il modello implementare IValidatableObject non sembra funzionare, perché Validate(..) non ottiene chiamato .ModelState.IsValid vs IValidateableObject in MVC3

Qualcuno sa se c'è qualcos'altro che devo cablare per farlo funzionare?

EDIT:

Ecco il codice come richiesto.

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'azione. (Non faccio nulla di speciale nel Controller ...)

[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); 
} 

Ho impostato un punto di interruzione sulla prima riga del LoginModel.Validate() e non sembra essere colpito.

+0

Il codice sembra perfetto. Esattamente come dovrebbe Solo un punto di interesse, ma hai un modello duplicato? So di avere un modello di vista e un modello db per ogni oggetto. Il tuo controller potrebbe fare riferimento al modello sbagliato? – Buildstarted

+2

Inoltre, come nota a margine: è necessario restituire un solo errore se il nome utente o la password non sono validi e non sono errori distinti. Questo è semplicemente per sicurezza, poiché posso testare ogni campo individualmente per trovare un nome utente e poi lavorare sulla password per quell'utente. Non è obbligatorio ma è una buona idea :) – Buildstarted

+0

Si potrebbe usare 'yield return DataContext ...' piuttosto che restituire una nuova lista. Sarebbe più carino e più veloce. – pipedreambomb

risposta

18

Non c'è niente di più di quello che devi aggiungere al modello che stai convalidando. Ecco un esempio di convalida

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>(); 
    } 
} 

E il controller avrebbe usato questo modello

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

Spero che questo aiuti. Altri requisiti sono .net 4 e annotazioni di dati - che ovviamente avete bisogno di jsut per l'oggetto ivalidiabile. Pubblica eventuali problemi e vedremo se non riusciamo a risolverli, ad esempio pubblicare il modello e il controller ... potresti perdere qualcosa.

+23

Sei corretto, l'unica avvertenza è che se hai attributi di convalida che comportano l'invalidità del modello, Convalida non viene mai chiamato. Questo era il mio problema. –

+0

Ah, che interessante. Grazie per quello. – Buildstarted

+2

Questo avvertimento mi sembra una cosa molto utile da sapere :) +1 a entrambi –

6

Validazione tramite DefaultModelBinder è un processo a due fasi. Innanzitutto, sono convalidati Data Annotations. Quindi (e solo se la convalida delle annotazioni dei dati ha generato zero errori), viene chiamato IValidatableObject.Validate(). Tutto avviene automaticamente quando la tua azione post ha un parametro viewmodel. ModelState.IsValid non fa nulla di simile. Piuttosto, indica solo se un elemento nella collezione ModelState ha un valore non vuoto ModelErrorCollection.

+0

Mi sono sempre chiesto questo. Se validation è stato chiamato automaticamente in DefaultModelBinder, o se ModelState.IsValid ha controllato alcune proprietà '_hadBeenValidated' nascoste e ha chiamato validate if' _hasBeenValidated == false'. –

+2

Il comportamento descritto non è esattamente quello reale: consultare http://stackoverflow.com/questions/8153602/ivalidatableobject-validate-combined-with-dataannotations – Diego