2010-09-19 17 views
15

lo que de acuerdo a GuIValidatableObject.Validate() debería ser llamado cuando un controlador valida su modelo (es decir, antes ModelState.IsValid) sin embargo, simplemente haciendo que el modelo de implementar IValidatableObject no parece funcionar, porque Validate(..) no recibe llamados .ModelState.IsValid vs IValidateableObject en MVC3

¿Alguien sabe si hay algo más que deba cablear para que funcione?

EDIT:

Aquí está el código conforme a lo solicitado.

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

La acción. (No hago nada especial en el controlador ...)

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

puse un punto de quiebre en la primera línea de LoginModel.Validate() y no parece que le peguen.

+0

Tu código se ve bien. Exactamente como debería. Solo un punto de interés, pero ¿tienes un modelo duplicado? Sé que tengo un modelo de vista y un modelo de base de datos para cada objeto. ¿Podría su controlador hacer referencia al modelo incorrecto? – Buildstarted

+2

Además, como nota al margen: definitivamente debe devolver solo un error si el nombre de usuario o la contraseña no son válidos y no son distintos. Esto es simplemente por seguridad, ya que puedo probar cada campo individualmente para encontrar un nombre de usuario y luego trabajar en la contraseña para ese usuario. No es obligatorio, pero es una buena idea :) – Buildstarted

+0

Puede usar 'yield return DataContext ...' en lugar de devolver una nueva lista. Sería más bonito y más rápido. – pipedreambomb

Respuesta

18

No hay nada más que eso, solo tiene que agregarlo al modelo que está validando. Aquí está un ejemplo de validación

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

Y su controlador usaría este modelo

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

Espero que esto ayude. Otros requisitos son .net 4 y anotaciones de datos, que obviamente necesitas jsut para el objeto ivalidatable. Publique cualquier problema y veremos si no podemos resolverlo, como publicar su modelo y su controlador ... es posible que se pierda algo.

+23

Tiene razón, la única advertencia es que si tiene algún atributo de validación que provoque que el modelo no sea válido, nunca se llama a Validate. Ese fue mi problema. –

+0

Ah, qué interesante. Gracias por eso. – Buildstarted

+2

Esa advertencia me parece muy útil :) +1 a ambos –

6

La validación usando DefaultModelBinder es un proceso de dos etapas. Primero, Data Annotations están validados. Luego (y solo si la validación de las anotaciones de datos resultó en cero errores), se llama al IValidatableObject.Validate(). Todo esto tiene lugar automáticamente cuando su acción de publicación tiene un parámetro viewmodel. ModelState.IsValid no hace nada como tal. Más bien solo informa si algún elemento en la colección ModelState tiene ModelErrorCollection no vacío.

+0

Siempre me pregunté sobre eso. Si la validación se llamó automágicamente en DefaultModelBinder, o si ModelState.IsValid verificó alguna propiedad oculta '_hadBeenValidated' y llamó validate si' _hasBeenValidated == false'. –

+2

El comportamiento descrito no es exactamente el real: consulte http://stackoverflow.com/questions/8153602/ivalidatableobject-validate-combined-with-dataannotations – Diego

Cuestiones relacionadas