2010-09-19 19 views
15

więc według GuIValidatableObject.Validate() powinien uzyskać wywołana, gdy kontroler sprawdza, to wzór (czyli przed ModelState.IsValid), jednakże po prostu czyni model wdrożenia IValidatableObject nie wydają się działać, bo Validate(..) nie sprawdzony .ModelState.IsValid vs IValidateableObject w MVC3

Ktoś wie, czy jest coś innego, co muszę podłączyć, aby to zadziałało?

EDIT:

Oto kod na żądanie.

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

Akcja. (Nie mam nic specjalnego robić w kontrolerze ...)

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

ustawić punkt przerwania na pierwszej linii LoginModel.Validate() i nie wydaje się, aby oberwać.

+0

Twój kod wygląda dobrze. Dokładnie tak, jak powinno. Tylko punkt zainteresowania, ale czy masz zduplikowany model? Wiem, że mam model widoku i model db dla każdego obiektu. Czy Twój kontroler może odwoływać się do niewłaściwego modelu? – Buildstarted

+2

Również na marginesie: z pewnością powinieneś zwrócić tylko jeden błąd, jeśli nazwa użytkownika lub hasło jest nieprawidłowe i nie ma wyraźnych błędów. Jest to po prostu dla bezpieczeństwa, ponieważ mogę przetestować każde pole indywidualnie, aby znaleźć nazwę użytkownika, a następnie pracować nad hasłem dla tego użytkownika. Nie jest to wymagane, ale jest to dobry pomysł :) – Buildstarted

+0

Można użyć 'zwrotu dochodu DataContext ...' zamiast zwracania nowej listy. Byłaby ładniejsza i szybsza. – pipedreambomb

Odpowiedz

18

Nie ma nic więcej niż to, że wystarczy dodać go do modelu, który walidujesz. Oto przykład walidacji

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

i kontroler będzie korzystać z tego modelu

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

nadzieję, że to pomaga. Inne wymagania to .net 4 i adnotacje danych - które oczywiście potrzebujesz jsut dla ivalidatableobject. Opublikuj wszelkie problemy, a zobaczymy, czy nie możemy ich rozwiązać - np. Po opublikowaniu modelu i kontrolera ... możesz czegoś nie zauważyć.

+23

Masz rację, jedynym zastrzeżeniem jest to, że jeśli masz jakieś atrybuty sprawdzania poprawności, które powodują, że model jest nieważny, sprawdzanie poprawności nigdy nie zostanie wywołane. To był mój problem. –

+0

Ach, jakie to interesujące. Dziękuję za to. – Buildstarted

+2

To zastrzeżenie uderza mnie jako bardzo przydatna informacja :) +1 do obu –

6

Walidacja za pomocą DefaultModelBinder jest procesem dwustopniowym. Po pierwsze, Data Annotations są sprawdzane. Następnie (i tylko wtedy, gdy weryfikacja adnotacji danych zakończyła się błędem zero), wywoływana jest nazwa IValidatableObject.Validate(). Wszystko to dzieje się automatycznie, gdy twoja akcja post ma parametr viewmodel. ModelState.IsValid nic nie robi. Raczej po prostu zgłasza, czy jakikolwiek element w kolekcji ModelState ma niepustą wartość ModelErrorCollection.

+0

Zawsze się nad tym zastanawiałem. Czy sprawdzanie poprawności nazwano automagicznie w DefaultModelBinder, czy też ModelState.IsValid zaznaczyło pewną ukrytą właściwość '_hadBeenValidated' i wywołało sprawdzanie poprawności, jeśli' _hasBeenValidated == false'. –

+2

Opisane zachowanie nie jest zgodne z rzeczywistością: zobacz http://stackoverflow.com/questions/8153602/ivalidatableobject-validate-combined-wa-daneannotations – Diego