I have used the jquery validation plug in for a couple years now but this is my first effort to mixing with MVC 3 unobtrusive validation.
Information on the client s
Here's how you could proceed. First you need to write a custom validator attribute in order to ensure that the validation is enforced on the server side. You could use the one described in this 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"
};
}
}
Notice that I've modified it to make it implement the IClientValidatable interface and added the GetClientValidationRules method which simply uses the same error message for client validation as the server side and provides an unique name for this validator that will be used by the jquery unobtrusive adapter. Now all that's left is to decorate your model property with this attribute:
[CreditCard(ErrorMessage = "Please enter a valid credit card number")]
[Required]
public string CardNumber { get; set; }
and in your view:
<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" />
}
You need ValidateFor
in addition to TextBoxFor
. I can't tell from your question if you've already done that or not. You also need EnableClientValidation
before the form.