5 AS SP.NET MVC Walidacja danych 1
1. Cel zajęć Celem zajęć jest zapoznanie się z metodami walidacji danych wprowadzanych przez użytkownika oraz z tworzeniem własnych walidatorów. 2. Zadanie Proszę przebudować aplikację, która była utworzona na poprzednich zajęciach, tak aby wykorzystywała elementy widoków wspomnianych w poprzednim punkcie. W tym celu należy wykonać odpowiednie kroki: I. Rozbudowanie modelu 1. Otwórzmy projekt z poprzednich zajęć. 2. W oknie Solution Explorer klikamy dwukrotnie na pliku zawierającym naszą bazę danych. 3. W otwartym oknie Server Explorer klikamy dwukrotnie na tabeli Osoba i dodajemy do niej dwie kolumny: DataUrodzenia (DataType: Date, AllowNull true) NrPesel (DataType: varchar(11), AllowNull true) 4. Po modyfikacji struktury tabeli należy odpowiednio dostosować przygotowane wcześniej mapowanie LinqToSql. W tym celu otwieramy plik zawierający to mapowanie, a następnie kliknąć prawym przyciskiem myszy na nagłówku obiektu reprezentującego tabelę Osoba i wybrać opcję Add > Property. 5. Spowoduje to dodanie nowej właściwości, której nadajemy taką samą nazwę jaką nadaliśmy polu w tabeli. 6. Powtarzamy kroki 4 i 5 dla drugiego dodanego pola. 2
7. Klikając prawym przyciskiem myszy na nazwie właściwości wybieramy opcję Properties i w oknie Properties ustawiamy właściwości: Właściwość: DataUrodzenia Właściwość: NrPesel Type DateTime (System.DateTime) Type string (System.String) Nullable True Nullable True Server Data Date Type Server Type Dataa VarChar(11) Source DataUrodzenia Source NrPesel 8. Modyfikujemy klasę OsobaModel tak aby w rezultaciee uzyskać kod (jeżeli mamy obecnie przypisane do poszczególnych pól jakieś atrybuty, możemy je pozostawić): public classs OsobaModel public int ID get; set; public string Imie get; set; [DisplayName("Drugie imię")] public string DrugieImie get; set; public string Nazwisko get; set; public string NrPesel get; set; public DateTime? DataUrodzenia get; set; 9. Modyfikujemy kod metod w klasie implementującej interfejs IOsobaUslugi, tak aby odzwierciedlić wprowadzone wcześniej zmiany. II. Wykorzystanie walidatorów wbudowanych Najpowszechniejszą metodą walidacji danych wejściowych w ASP.NET MVC jest zastosowanie atrybutów walidujących. W punkcie tym zastosujemy tę metodę do modelu OsobaModel, przy czym wykorzystamy atrybuty dostarczane z frameworkiem. Uwaga! Do pliku zawierającego klasy modelu należy System.ComponentModel.DataAnnotations dołączyć przestrzeń nazw 1. Projektując bazę danych określiliśmy, że pole Nazwisko jest wymagane. W celu sprawdzenia czy użytkownik rzeczywiście podał tę wartość i ewentualnemu wyświetleniu informacji o błędzie należy właściwość Nazwisko oznaczyć za pomocą atrybutu Required. [Required] public string Nazwisko get; set; 3
2. Dodatkowoo w bazie danych określiliśmy maksymalną liczbę znaków jakie mogą zostać wprowadzone do pól Imie, DrugieImie, Nazwisko. Sprawdzenie tego ograniczenia odbywa się za pomocą atrybutu StringLength (przy czym w tym przypadku należy podać maksymalną liczbę znaków jakie właściwość może zawierać. Oznaczając poszczególne atrybuty powinniśmy otrzymać następujący kod: [StringLength(100)] public string Imie get; set; [DisplayName("Drugie imię")] [StringLength(100)] public string DrugieImie get; set; [Required] [StringLength(100)] public string Nazwisko get; set; 3. Zgodnie z tak przygotowanym modelem framework ASP.NET MVC będzie przeprowadzał walidację danych, jednak użytkownikk na ekranie zobaczy (po dostosowaniu kontrolera i widoku) domyślne komunikaty w języku angielskim. Ich treść można zmienić dodając do każdego atrybutu parametr ErorrMessage, co ostateczniee prowadzi do kodu: [StringLength(100, ErrorMessage="Zbyt dużo znaków")] public string Imie get; set; [DisplayName("Drugie imię")] [StringLength(100, ErrorMessage="Zbyt dużo znaków")] public string DrugieImie get; set; [Required(ErrorMessage="To pole jest wymagane")] [StringLength(100, ErrorMessage="Zbyt dużo znaków")] public string Nazwisko get; set; 4. Kolejnym krokiem do uruchomienia walidacji jest zmodyfikowanie akcji kontrolera. Informacje o tym czy walidacja przebiegła pomyślnie czy nastąpił jakiś błąd można uzyskać wykorzystując właściwość IsValid obiektu ModelState np. [HttpPost] public ActionResult Edit(int id, OsobaModel osoba) try if (ModelState.IsValid) _osoby.edytujosobe(osoba); return RedirectToAction("Index"); el lse return View(); catach return View(); 4
5. W przypadku, gdy widok był wygenerowany jako Strongly Typed View przez Visual Studio zawiera cały potrzebny kod do wygenerowania informacji o błędzie. Jednak należy pamiętać o dołączeniu kodu HTML do obsługi nowych pól dodanych do tabeli Osoba. III. Numer PESEL Numer PESEL (Powszechny Elektroniczny System Ewidencjii Ludności) jest unikatowym identyfikatorem jednoznacznie identyfikującym osobę fizyczną. Składa się on z 11 cyfr, z których: Cyfry [1-6] określają datę urodzenia z uwzględnieniem stulecia Cyfry [7-10] numer porządkowy uwzględniający płeć (liczba parzysta oznacza płeć żeńską, liczba nieparzysta męską). Cyfra [11] stanowi liczbę kontrolną. Data urodzenia w numerze PESEL zapisywana jest w formacie (RRMMDD gdzie: RR dwie ostatnie cyfry roku urodzenia, MM miesiąc z wiodącym zerem, DD dzień z wiodącym zerem). Dla odróżnienia poszczególnych stuleci przyjęto następującą konwencję: Dla osób urodzonych w latach 1800 1899 do numeru miesiąca dodaje się wartość 80 Dla osób urodzonych w latach 1900 1999 do numer miesiąca zapisuje się w sposób naturalny Dla osób urodzonych w latach 2000 2099 do numeru miesiąca dodano wartość 20 Dla osób urodzonych w latach 2100 2199 do numeru miesiąca dodano wartość 40 Dla osób urodzonych w latach 2200 2299 do numeru miesiąca dodano wartość 60 W celu wyznaczenia cyfry kontrolnej, z pierwszych 10 cyfr numeru PESEL należy obliczyć sumę ważoną. Wagi odpowiadające poszczególnym cyfrom przedstawia poniższa tabela: 1 2 3 1 3 7 4 5 6 7 8 9 10 9 1 3 7 9 1 3 Po obliczeniu sumy uzyskaną wartość należy podzielić moduloo 10 odjąć od 10 i ponownie podzielić modulo 10. Uzyskana wartość stanowi cyfrę kontrolną. Algorytm ten można wykorzystać również do weryfikacji numeru PESEL. 5
IV. Stworzenie walidatorów pozwalających walidację pojedynczej właściwości na 1. Na początku stwórzmy walida tor, który sprawdzi czy długość tekstu w polu NrPesel ma odpowiednią długość. 2. W tym celu dodajmy do projektu (do katalogu Models) nową klasę DokladnaDlugoscAttribute, która będzie dziedziczyć po klasie ValidationAttribute. 3. Następnie do klasy tej dodajmy prywatne pole typu int, które będzie przechowywać wymaganą długość łańcucha znaków oraz publiczny konstruktor, który będzie ustawiał tę wartość podczas tworzenia obiektu. 4. Następnie nadpisujemy metodę wirtualną IsValid, która jest odpowiedzialna za dokonanie sprawdzenia poprawności danych. 5. Cała klasa powinna wyglądać następująco public class DokladnaDlugoscAttribute : ValidationAttribute int _dlugosc; public DokladnaDlugoscAttribute(int dlugosc) _d dlugosc = dlugosc; public override bool IsValid(object value) if (value == null) return true; if ((value as string).trim().length == _dlugosc) return true; else return false; Należy zwrócić uwagę na fragment: if (value == null) return true; który uznaje, że wartość null jest wartością poprawną. Taki fragment powinien się znaleźć w każdym walidatorze, który sprawdza wartość pojedynczej właściwości, gdyż po pierwsze nie wiemy, czy pole wartość tego pola będzie wymagana, czy nie, a po drugie od sprawdzania czy użytkownik podał daną wartość jest atrybut Required. 6. Proszę stworzyć dodatkowy walidator, który będzie sprawdzał, czy numer pesel jest poprawny względem cyfry kontrolnej. 7. Walidatory tego typu wywołujemy dokładnie w ten sam sposób, co przedstawione w punkcie II walida tory wbudowane. 6
V. Stworzenie walidatora pozwalającego na walidację danych z powiązanych pól 1. Algorytm do obliczania cyfry kontrolnej numeru PESEL ma pewną wadę, która w łatwy sposób może spowodować wprowadzeniee błędnego numeru (kto z Państwa ją znajdzie?). Jej korekta wymaga jednak wprowadzania daty urodzenia osoby i porównywania jej z numerem PESEL. Takie sprawdzenie uwzględnia dwa pola modelu przez co wykorzystanie klasy działającej na poziomie właściwości może nie być zbyt wygodne. Zdecydowanie lepszym rozwiązaniem jest stworzenie walidatora działającego na poziomie klasy modelu. 2. W tym celu dodajmy do projektu nową klasę np. PeselModelAttribute, której kod przedstawiony jest poniżej: public class PeselModelAttribute : ValidationAttribute public string Pole_DataUrodzenia get; private set; public string Pole_Pesel get; private set; public PeselModelAttribute( ss string pole_dataurodzenia, string pole_pesel) Pole_DataUrodzenia = pole_dataurodzenia; Pole_Pesel = pole_pesel; public override bool IsValid(object value) PropertyDescriptorCollection wlasciwosci = TypeDescriptor.GetProperties(value); DateTime? dataurodzenia = (DateTime?)(wlasciwosci.Find(Pole_DataUrodzenia, true).getvalue(value)); string pesel = wlasciwosci.find(pole_pesel, true).getvalue(value) as string; int miesiac = dataurodzenia.value.month; /// W tym miejscu należy umieścić odpowiedni kod /// realizujący walidację zgodnie z /// opisem umieszczonym w punkcie II II W klasie tej najciekawszy jest początek metody IsValid, która w tym przypadku wykorzystuje mechanizm refleksji do uzyskania danych określonych właściwości. Nazwy właściwości, których wartości będą porównywane określone są w konstruktorze. Klasa PropertyDescriptorCollection służy do przechowywania kolekcji obiektów opisujących metainformacje o właściwościach. Kolekcję tę uzyskuje się wywołując metodę GetProperties klasy TypeDescriptor. Metoda ta jako parametr przyjmuje obiekt, z którego mają być pobrane właściwości. 7
W kolekcji tej wyszukujemy po nazwie odpowiedniej właściwości (metoda Find), a następnie pobieramy jej wartość metodą GetValue, która jako parametr przyjmuje obiekt, z którego wartość ma być odczytana. Ponieważ metoda ta zwraca obiekt typu object, musi on być zrzutowany na odpowiedni typ danych. Walidator umieszczamy przed całą klas, jak pokazanoo poniżej: [PeselModel("DataUrodzenia", "NrPesel", ErrorMessage="Data urodzenia nie zgadza się z datą w numerze PESEL")] public class OsobaModel IV. Zadania do samodzielnego wykonania 1. Proszę do tabeli Osoba dołączyć pole Plec. I napisać walidator, który będzie sprawdzał czy wybrana przez użytkownika płeć będzie zgodna z tą zakodowaną w numerzee PESEL. 2. Proszę o napisanie walidatora, który będzie sprawdzał czy w bazie danych występuje już wprowadzony numer telefonu, tak aby był on unikalny (oczywiście można to zapewnić ustawiając odpowiedni klucz na tabeli i przechwytując ewentualny wyjątek, ale aby poćwiczyć tworzenie walidatorów, nie będziemy korzystali z tego rozwiązania). 8