Validering i ASP.NET MVC 2
Tack vare att ASP.NET MVC 2 använder sig utav Data Annotations så kan v enkelt använda oss utav dessa för att få till validering på våra sidor. Vi kan på enskilda fält sätta att till exempel värdet måste vara ett tal 10-20, att det inte får vara tomt m.m.
För att komma igång med exemplet som jag kommer att använda mig utav så skapa först en enkel modell:
using System.ComponentModel.DataAnnotations;
using MvcValidate.Code;
namespace MvcValidate.Models
{
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
Vi behöver sedan en controller för att hantera alla Customer-objekt.
Kom ihåg att nedan inte är best practices, utan är bara en enkel controller som gör livet enklare för oss i exemplet.
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MvcValidate.Models;
namespace MvcValidate.Controllers
{
public class CustomerController : Controller
{
public ActionResult Index()
{
return View(Customers.GetCustomers());
}
public ActionResult Details(int id)
{
Customer customer = Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault();
return View(customer);
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Customer customer)
{
try
{
Customers.GetCustomers().Add(customer);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
public ActionResult Edit(int id)
{
Customer customer = Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault();
return View(customer);
}
[HttpPost]
public ActionResult Edit(int id, Customer customer)
{
Customer currentCustomer = Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault();
try
{
UpdateModel(currentCustomer);
var yadda = Customers.GetCustomers();
return RedirectToAction("Index");
}
catch
{
return View(Customers.GetCustomers().Where(c => c.Id == id).FirstOrDefault());
}
}
}
internal static class Customers
{
private static List<Customer> customers;
public static List<Customer> GetCustomers()
{
if (customers != null)
return customers;
customers = new List<Customer>()
{
new Customer()
{
Id = 1,
Name = "Mikael",
Age = 24
},
new Customer()
{
Id = 2,
Name = "Nisse",
Age = 54
}
};
return customers;
}
}
}
Förutom olika Action-metoder för olika scenarion så har jag lagt till en statisk klass som innehåller lite dummydata. Anledningen till att jag har den statisk är för att vi enkelt ska kunna se vilka ändringar som görs.
Nästa steg är att skapa upp vyer för alla Action-metoder. Skapa standardvyer för List, Create osv, och typa dem mot Customer som vi skapade tidigare.
Det vi har nu är en enkel sida baserad på ASP.NET MVC, vilken tillåter oss att lista, skapa och ändra kunder.
Få igång servervalidering
Ett problem i koden ovan är att värdena kan sättas lite vilt, då vi inte har någon kontroll över dem. För att se till att vi alltid har korrekt värden så kan vi använda Data Annotations, vilket låter oss sätta olika regler på olika fält. Dessa slår sedan igenom över hela sidan på de specifika fälten, vilket gör det enkelt att modifiera alla dessa i efterhand.
För att få till validering på vår befintliga modell så uppdaterar vi den så att den ser ut så här:
using System.ComponentModel.DataAnnotations;
using MvcValidate.Code;
namespace MvcValidate.Models
{
public class Customer
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(18, 65)]
[Required]
public int Age { get; set; }
}
}
I System.ComponentModel.DataAnnotations så har vi de attribut som används för de olika fälten.
Required-attributet visar att det aktuella fältet måste ha ett värde, annars går det inte igeom. Attributet Range som vi har satt på Age-fältet förklarar att värdet på Age måste vara en integer med ett värde mellan 18 och 65.
Det går utmärkt att använda flera attribut på ett enskilt fält, som vi gör för Age.
När vi har satt dessa attribut och kör sidan så har vi automatiskt fått validering av det som skickas in till modellen. Försöker vi t.ex. sätta ett tomt värde på Name och talet ”17” på Age så får vi det här resultatet när vi postar:
Vi får nu alltså automatisk validering på våra fält, bara genom att sätta olika attribut på dem.
Få igång klientvalidering
En nackdel med valideringen ovan är att den kräver att vi postar sidan. Det absolut bästa, både för användarupplevelsen och för prestandan är att ingen postning görs, utan att vi direkt får information om att värdet är fel. För att få det gjort så kan vi använda oss utav den klientvalidering som finns i ASP.NET MVC 2.
För att aktivera klientvalidering på sidan så lägger vi till det här innan Html.BeginForm() i Edit.aspx:
<% Html.EnableClientValidation(); %>
Starta sidan igen efter ändringen. Plocka nu bort värdet för Name i edit-sidan och tryck på tab. Nu kan vi på en gång se att värdet är felaktigt., utan att behövs posta om sidan.
Så hur fungerar det här?
Om vi kikar i källkoden för den renderade sidan så kan vi se att det finns en Json-sträng inbakad på sidan. Då alla mellanslag och radbrytningar är borttagna så kan det vara svårt att se vad som står, men genom att lägga till dessa så kan vi se att det som har lagts in är det här:
Sys.Mvc.FormValidation.enableClientValidation({
"Fields": [{
"FieldName": "Name",
"ReplaceValidationMessageContents": true,
"ValidationMessageId": "form0_Name_validationMessage",
"ValidationRules": [{
"ErrorMessage": "The Name field is required.",
"ValidationParameters": {},
"ValidationType": "required"
}]
},
{
"FieldName": "Age",
"ReplaceValidationMessageContents": true,
"ValidationMessageId": "form0_Age_validationMessage",
"ValidationRules": [{
"ErrorMessage": "The field Age must be between 18 and 65.",
"ValidationParameters": {
"minimum": 18,
"maximum": 65
},
"ValidationType": "range"
},
{
"ErrorMessage": "The Age field is required.",
"ValidationParameters": {},
"ValidationType": "required"
}]
}],
"FormId": "form0"
},
null);
Det är alltså Json som innehåller de fält som skall valideras, samt information om hur valideringen skall gå till.
De intressanta bitarna här är ErrorMessage och ValidationParameters. För Name så finns inga ValidationParameters då den bara skall se till så att vi har värden, men för Age så har vi parametrar som används i bakgrunden för att se till så att de regler som vi själva har satt, stämmer överens med de som har skickats in.
Det som genereras är alltså baserat på det som vi själva har angett i modellen.
Om något blir fel så visas ErrorMessage på sidan.
Då det är ett rent JavaScript-objekt så innebär det att vi själva skulle kunna fånga upp det och använda för egen validering.
Skapa en anpassad validering
Det händer ofta att man har egna specialscenarion där man vill ha en anpassad validering då det inte finns något inbyggt för det. Som ett exempel så kan vi kräva att det finns ett visst antal mellanslag för namnen på vår sida.
Det första vi ska göra nu för att få till valideringen är att skapa själva attributet som skall sättas på fälten.
using System;
using System.ComponentModel.DataAnnotations;
namespace MvcValidate.Code
{
public class NameAttribute : ValidationAttribute
{
public int Spaces { get; set; }
public override bool IsValid(object value)
{
string name = value as string;
int spaces = 0;
if (String.IsNullOrEmpty(name))
return false;
foreach (char item in name)
{
if (item.Equals(' '))
spaces++;
}
return spaces.Equals(Spaces);
}
}
}
Istället för att ärva direkt från Attribute som vi normalt gör med attribut, så ärver vi från ValidationAttribute. ValidationAttribute innehåller ett par olika metoder, men den enda vi behöver är IsValid() som kollar om det inskickade värdet är giltigt. Vi har även med en egenskap ”Spaces”, vilken innehåller antalet mellanslag som måste finnas.
Det som sker i IsValid är att vi ser så att vi har rätt antal mellanslag i det inskickade värdet.
Vi måste sedan lägga till attributet på vårt fält:
[Name(ErrorMessage="Du måste ha ett mellanslag.", Spaces=1)]
public string Name { get; set; }
Om vi nu kör sidan så får vi upp det här när vi postar ett namn utan mellanslag:
Nu får vi anpassad validering som slår igenom när vi postar sidan. För att kunna använda klientvalidering så måste vi se till att även skriva en sådan.
Till att börja med så behöver vi en Validator för modellen. Den används för att skicka våra regler med dess parametrar till klienten.
using System.Collections.Generic;
using System.Web.Mvc;
namespace MvcValidate.Code
{
public class NameValidator : DataAnnotationsModelValidator<NameAttribute>
{
public NameValidator(ModelMetadata metadata, ControllerContext context, NameAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rules = new List<ModelClientValidationRule>();
var rule = new ModelClientValidationRule()
{
ErrorMessage = Attribute.ErrorMessage,
ValidationType = "name"
};
rule.ValidationParameters.Add("spaces", Attribute.Spaces);
rules.Add(rule);
return rules;
}
}
}
Genom att köra en override på GetClientValidationRules så kan vi själva ange vad som skall skickas till klienten. Det är det här som dyker upp i den Json som vi såg tidigare.
Vi behöver sedan registrera det här i global.asax under Application_Start.
DataAnnotationsModelValidatorProvider
.RegisterAdapter(typeof(NameAttribute), typeof(NameValidator));
Här registrerar vi NameAttribute och NameValidator så att de skall finnas med i klientvalideringen. Utan det här så kommer inte valideringsreglerna med.
Det sista steget för att få till valideringen är att skriva JavaScriptet som skall användas för valideringen.
Börja med att skapa en js-fil i Scripts-mappen. Här ska vi använda oss utav ValidatorRegistry som finns i MicrosoftMvcAjax.js.
var validator = Sys.Mvc.ValidatorRegistry.get_creators();
validator["name"] = function(rule) {
console.log('Validator');
var spaces = rule.ValidationParameters["spaces"];
console.log('Spaces: ' + spaces);
var validator = {
validate: function(validation, elements, value) {
var currentSpaces = (value.split(' ').length - 1);
console.log('Value: ' + value);
console.log('currentSpaces: ' + currentSpaces);
console.log('spaces: ' + spaces);
if (currentSpaces < spaces) {
return [rule.ErrorMessage];
}
return null;
}
};
return validator;
};
Här har vi en JavaScript-version av valideringen. Jag har även med loggning för att vi ska kunna se i consolen så att alla värden ser rätt ut.
JavaScriptet ovan körs varje gång vi skriver ett tecken, vilket gör att vi snabbt och enkelt kan se om värdet är giltigt. Man bör dock inte använda något Ajax-anrop, då det kan leda till väldigt mycket overhead.
Om vi infogar js-filen på sidan så kan vi se att vi nu har realtidsvalidering av det vi skriver i textrutan.
Vi kan även se i consolen (kräver Firebug för Firefox eller Internet Explorer 8) om allt stämmer:
Utan något större arbete så kunde vi skapa en anpassad validering som fungerar på både servern och klienten, tillsammans med den inbyggda valideringsfunktionaliteten i ASP.NET MVC 2.