2011-01-28 18 views
17

He utilizado el complemento de validación jquery desde hace un par de años, pero este es mi primer esfuerzo para mezclar con la validación discreta MVC 3.ASP .Net MVC 3 validación de clientes personalizados no intrusiva

La información del lado del cliente se encuentra dispersa en Internet y es difícil encontrar algo lo suficientemente profundo para explicarla a las personas que aún no la han utilizado. Estuve consumiendo Google durante una hora para obtener un ejemplo sobre cómo crear un validador personalizado del lado del cliente.

@Html.TextBoxFor(model => Model.CCPayment.CardNumber, new { @class = "textInput validateCreditCard", maxLength = "20" }) 

$(document).ready(function() { 
     jQuery.validator.unobtrusive.adapters.add('validateCreditCard', {}, function (value, element) { 
     alert('foo'); 
    }); 
}); 

si ejecuto el código anterior en la parte inferior de mi vista, no hace absolutamente nada. Incluso he intentado con jquery.validator.addmethod() y aún nada. Toda la validación del lado del cliente que se emite desde mi modelo de anotaciones de validación funciona bien.

<div class="ctrlHolder"> 
       <label> 
        <em>*</em> 
        Card Number: 
       </label> 
       @Html.TextBoxFor(model => Model.CCPayment.CardNumber, new { @class = "textInput validateCreditCard", maxLength = "20" }) 

       <p class="field-validation-valid formHint" data-valmsg-for="CCPayment.CardNumber"></p> 
      </div> 
+0

¿Incluyó el script "jquery.validate.unobtrusive.js" en su página? – Chandu

+0

sí ... todas las otras validaciones del lado del cliente funcionan, en mi modelo tengo algunas anotaciones que hacen que los campos sean necesarios y todos funcionan bien. Solo quiero agregar un método de validación personalizado a la validación del formulario. – JBeckton

Respuesta

26

Así es como podría proceder. Primero, debe escribir un atributo de validador personalizado para garantizar que la validación se aplique en el lado del servidor. Se podría utilizar la descrita en este blog post:

public class CreditCardAttribute : ValidationAttribute, IClientValidatable 
{ 
    private CardType _cardTypes; 
    public CardType AcceptedCardTypes 
    { 
     get { return _cardTypes; } 
     set { _cardTypes = value; } 
    } 

    public CreditCardAttribute() 
    { 
     _cardTypes = CardType.All; 
    } 

    public CreditCardAttribute(CardType AcceptedCardTypes) 
    { 
     _cardTypes = AcceptedCardTypes; 
    } 

    public override bool IsValid(object value) 
    { 
     var number = Convert.ToString(value); 

     if (String.IsNullOrEmpty(number)) 
      return true; 

     return IsValidType(number, _cardTypes) && IsValidNumber(number); 
    } 

    public override string FormatErrorMessage(string name) 
    { 
     return "The " + name + " field contains an invalid credit card number."; 
    } 

    public enum CardType 
    { 
     Unknown = 1, 
     Visa = 2, 
     MasterCard = 4, 
     Amex = 8, 
     Diners = 16, 

     All = CardType.Visa | CardType.MasterCard | CardType.Amex | CardType.Diners, 
     AllOrUnknown = CardType.Unknown | CardType.Visa | CardType.MasterCard | CardType.Amex | CardType.Diners 
    } 

    private bool IsValidType(string cardNumber, CardType cardType) 
    { 
     // Visa 
     if (Regex.IsMatch(cardNumber, "^(4)") 
      && ((cardType & CardType.Visa) != 0)) 
      return cardNumber.Length == 13 || cardNumber.Length == 16; 

     // MasterCard 
     if (Regex.IsMatch(cardNumber, "^(51|52|53|54|55)") 
      && ((cardType & CardType.MasterCard) != 0)) 
      return cardNumber.Length == 16; 

     // Amex 
     if (Regex.IsMatch(cardNumber, "^(34|37)") 
      && ((cardType & CardType.Amex) != 0)) 
      return cardNumber.Length == 15; 

     // Diners 
     if (Regex.IsMatch(cardNumber, "^(300|301|302|303|304|305|36|38)") 
      && ((cardType & CardType.Diners) != 0)) 
      return cardNumber.Length == 14; 

     //Unknown 
     if ((cardType & CardType.Unknown) != 0) 
      return true; 

     return false; 
    } 

    private bool IsValidNumber(string number) 
    { 
     int[] DELTAS = new int[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 0 }; 
     int checksum = 0; 
     char[] chars = number.ToCharArray(); 
     for (int i = chars.Length - 1; i > -1; i--) 
     { 
      int j = ((int)chars[i]) - 48; 
      checksum += j; 
      if (((i - chars.Length) % 2) == 0) 
       checksum += DELTAS[j]; 
     } 

     return ((checksum % 10) == 0); 
    } 

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
    { 
     yield return new ModelClientValidationRule 
     { 
      ErrorMessage = this.ErrorMessage, 
      ValidationType = "creditcard" 
     }; 
    } 
} 

Tenga en cuenta que he modificado para que sea implementa la interfaz IClientValidatable y añadí el método GetClientValidationRules que simplemente se utiliza el mismo mensaje de error de validación del cliente como del lado del servidor y proporciona un nombre único para este validador que será utilizado por el adaptador discreto jquery. Ahora todo lo que queda es para decorar su propiedad modelo con este atributo:

[CreditCard(ErrorMessage = "Please enter a valid credit card number")] 
[Required] 
public string CardNumber { get; set; } 

y en su opinión:

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script> 
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script> 
<script type="text/javascript"> 
    $(function() { 
     jQuery.validator.unobtrusive.adapters.addBool('creditcard'); 
    }); 
</script> 

@using (Html.BeginForm()) 
{ 
    @Html.TextBoxFor(x => x.CardNumber) 
    @Html.ValidationMessageFor(x => x.CardNumber) 
    <input type="submit" value="OK" /> 
} 
+0

Darin, gracias por la respuesta detallada. Estoy utilizando IValidatableObject en mi modelo en lugar de atributos personalizados. ¿Esto significa que no puedo vincular la validación del servidor y del lado del cliente a menos que use atributos personalizados? – JBeckton

+0

¿No puedo agregar la validación personalizada del cliente sin vincularla a la validación del lado del servidor? – JBeckton

+1

@JBeckton, podrías pero eso sería extremadamente inútil y una enorme vulnerabilidad de seguridad. Para que esto funcione, deberá tener los atributos HTML5 'data- *' adecuados emitidos en su cuadro de texto para que el adaptador funcione. Debe realizar su lógica de validación al menos en el servidor. La validación del lado del cliente es solo para fines cosméticos. –

2

Usted necesita ValidateFor además de TextBoxFor. No puedo decir por tu pregunta si ya has hecho eso o no. También necesita EnableClientValidation antes del formulario.

+0

EnableClientValidation se establece en mi web.config, eliminé el ValidatorFor helpers porque no son necesarios con la nueva validación no intrusiva de mvc 3. en su lugar, todo lo que necesita es un elemento html con una clase de validación de campo válida – JBeckton

+1

Si no tiene 'ValidateFor', no verá los mensajes de validación del lado del servidor. En MVC 2, la validación del lado del cliente tampoco funcionará sin ellos. –

+0

ok, tengo, si js está deshabilitado, no veré los mensajes de validación. Con está habilitado los veo pero como mensajes de validad del lado del cliente. – JBeckton

Cuestiones relacionadas