Realizacja Aplikacji Internetowych 2013 laboratorium cz. 2 K.M. Ocetkiewicz Walidacja po stronie klienta: - w MVC 3 i 4 domyślnie jest włączona także walidacja po stronie klienta - wykorzystuje ona JavaScript automatycznie dołączany do widoku (UWAGA: nie jest on dołączony do pustego projektu) - jeżeli jakiekolwiek pole będzie niepoprawne, formularz nie zostanie wysłany - walidacją sterujemy dodając atrybuty do pól modelu - [Required] mówi, że pole jest wymagane - [StringLength(max)] ustawia maksymalną długość napisu - [StringLength(max, MinimumLength=min)] ustawia minimalną i maksymalną długość - [Range(a, b)] ustawia zakres liczbowy - [RegularExpression(regex)] wymaga spełnienia wyrażenia regularnego - do każdego z tych atrybutów można dodać parametr ErrorMessage, którego wartością jest komunikat o błędzie - np. (klasa modelu): using System.Web.MVC; using System.Web.UI; public class Test [Required(ErrorMessage = "pole wymagane")] public string imie get; set; [RegularExpression(@"[A-Z][a-z]+", ErrorMessage="zły format")] public string nazwisko get; set; [StringLength(9, MinimumLength=4, ErrorMessage="len=4..9")] public string login get; set; [Required] [Range(0, 100)] public int wiek get; set; Zdalna walidacja (wykorzystująca AJAX): - możemy także walidować pola zdalnie - formularz wysyła zapytanie do serwera z wartością pola, serwer odpowiada obiektem true (jeżeli jest poprawne) lub komunikatem o błędzie (string) - należy dodać do web.config (w MVC 4 już to jest dodane): <appsettings> <add key="clientvalidationenabled" value="true" /> <add key="unobtrusivejavascriptenabled" value="true" /> </appsettings> 662BD5F6-0D63-4080-8769-62B3BA33A4501662BD5F6-0D63-4080-8769-62B3BA33A450
- w modelu nadajemy właściwości atrybut Remote z parametrami Controller i Action, które wskazują na kontroler i akcję, która dokonuje walidacji: [Remote( nazwa akcji, nazwa kontrolera )] - walidację wykonuje zwykła akcja (UWAGA: należy pamiętać, aby parametr akcji nazywał się tak samo jak pole w formularzu): - np.: class Model [Remote( ValidateStr2, Kontroler )] string str2; ; public JsonResult ValidateStr2(string str2) if(str2[0] == 'a' && str2[1] == 'b') // OK return Json(true, JsonRequestBehavior.AllowGet); else // błąd walidacji return Json("error message", JsonRequestBehavior.AllowGet); - w kontrolerze zawierającym tę akcję warto wyłączyć cacheowanie, dodając do kontrolera atrybut (więcej o cacheowaniu pod koniec instrukcji): [OutputCache(Location=OutputCacheLocation.None, NoStore=true)] atrybut HiddenInput (System.Web.Mvc) - parametr DisplayValue = true: w silnie typowanych widokach tylko prezentujących dane wyświetlana etykieta i wartość - parametr DisplayValue = false: w silnie typowanych widokach tylko prezentujących dane jako pole hidden - w silnie typowanych widokach pozwalających na edycję danych pole występuje jako pole hidden public class ProductViewModel [HiddenInput] // equivalent to [HiddenInput(DisplayValue=true)] public int Id get; set; public string Name get; set; [HiddenInput(DisplayValue=false)] public byte[] TimeStamp get; set; Obiekt Request - obiekt Request w akcji reprezentuje zapytanie - posiada między innymi pola: - HttpMethod, RequestType reprezentują metodę HTTP ( GET, POST itp.) - QueryString kolekcja parametrów zapytania (parametry typu GET) 662BD5F6-0D63-4080-8769-62B3BA33A4502662BD5F6-0D63-4080-8769-62B3BA33A450
- Form kolekcja parametrów zapytania (parametry typu POST) - Files kolekcja załączonych plików (nazwa obiekt pliku, z polami/metodami ContentType, ContentLength, FileName, SaveAs, InputStream) - Cookies kolekcja zawierający ciasteczka - ServerVariables kolekcja zmiennych serwera - Url adres zapytania (uwzględnia adres serwera) - RawUrl adres zapytania (nie uwzględnia adresu serwera) - Params połączone kolekcje Form, QueryString, Cookies i ServerVariables (może zawierać duplikaty) - UserAgent opis przeglądarki klienta - UserHostAddress adres klienta - UserLanguages uporządkowana tablica preferowanych języków Helpery - metody z przestrzeni nazw Html, które generują odpowiedni kod HTMLowy (zwracają napis), np.: <div class="editor-label"> @Html.LabelFor(model => model.name) </div> <div class="editor-field"> @Html.EditorFor(model => model.name) @Html.ValidationMessageFor(model => model.name) </div> - Html.ActionLink( tekst, akcja, [ kontroler, [new parametry, null]]) - Html.RouteLink( tekst, new controller =, action =, parametry=/*parametry*/ ) - Html.Encode() poprawnie formatuje napis do wyświetlenia w HTML (< na < itp.; Razor wykonuje to domyślnie) Formularze - Sposób 1 - Stworzyć obiekt zawierający pola, które są nam potrzebne (koniecznie jako właściwości) - Napisać akcję np.: public ActionResult Form() return View(); - Prawym add view strongly typed view ustawić klasę i elementy strongly typed view definiuje typ obiektu Model - Ewentualnie uzupełnić formularz (nazwy pól muszą być takie jak właściwości obiektu) - Napisać akcję: [HttpPost] public ActionResult Form(NazwaObiektu value) //i tu w value mamy zwartość pól formularza 662BD5F6-0D63-4080-8769-62B3BA33A4503662BD5F6-0D63-4080-8769-62B3BA33A450
- Sposób 2 - stworzyć formularz w widoku, - dodać akcję, która obsługuje dane z formularza (z atrybutem [HttpPost]), - skorzystać z obiektu Request.Form do odczytania danych Upload plików - W formularzu trzeba dodać enctype="multipart/form-data" (np. Html.BeginForm( nazwa akcji, nazwa kontrolera, FormMethod.POST, new enctype="multipart/form-data")) - Dodajemy <input type= file name= nazwa /> - W Request.Files mamy pliki: byte [] data = new byte[1024]; foreach(string name in Request.Files) // Request.Files[name].ContentType, // Request.Files[name].ContentLength // Request.Files[name].SaveAs, Request.Files[name].InputStream.Read(data, 0, 1024); string str = System.Text.Encoding.ASCII.GetString(data); - UWAGA: jeżeli plik jest przechowywany w modelu jako tablica bajtów, nazwa pliku w formularzu powinna być inna niż nazwa pola w modelu, inaczej MVC będzie próbowało przekazać jego zawartość w modelu, co się nie powiedzie i spowoduje rzucenie wyjątku Odsyłanie plików - w akcji zwracamy: return File(scieżka, mimetype[, nazwapliku]); lub return File(tablica bajtów, mimetype[, nazwapliku]); Asynchroniczne akcje - kontroler musi dziedziczyć po AsyncController zamiast Controller - dwie metody na każdą akcję: - void AkcjaAsync(parametry_akcji) - ActionResult AkcjaCompleted(parametry_inne) - AkcjaAsync powinna zwiększyć licznik oczekujących operacji: - AsyncManager.OutstandingOperations.Increment([opcjonalnie_o_ile]) - gdy operacja się zakończy, należy wywołać - AsyncManager.OutstandingOperations.Decrement() - gdy licznik OutstandingOperations dojdzie do zera, wywoływane jest AkcjaCompleted - AsyncManager.Parameters słownik parametrów do AkcjaCompleted (parametry_inne) 662BD5F6-0D63-4080-8769-62B3BA33A4504662BD5F6-0D63-4080-8769-62B3BA33A450
- parametry można też odczytać bezpośrednio z AsyncManager.Parameters - UWAGA: w adresie HTTP akcja nazywa się Akcja a nie AkcjaAsync public class AsyncTestController : AsyncController public void IndexAsync(int id) AsyncManager.OutstandingOperations.Increment(); // długą operację symulujemy timerem var t = new System.Timers.Timer(); t.autoreset = false; t.interval = 4000; // [ms] t.elapsed += new System.Timers.ElapsedEventHandler((evt, args) => AsyncManager.Parameters["value"] = 321; AsyncManager.OutstandingOperations.Decrement(); ); t.start(); public ActionResult IndexCompleted(int value) // parametr jest przekazany bezpośrednio do wywołania, // można też odczytać go tak: //ViewData["id"] = AsyncManager.Parameters["value"]; ViewData["id"] = value; return View(); Lokalizacja - do lokalizacji aplikacji korzystamy z zasobów (add new item Visual C# / General resources file) - UWAGA: w każdym pliku zasobów należy ustawić access modifier na public: - dodajemy do projektu pliki zasobów o nazwach: - nazwa.resx - nazwa.kultura.resx 662BD5F6-0D63-4080-8769-62B3BA33A4505662BD5F6-0D63-4080-8769-62B3BA33A450
- nazwa określa nam nazwę klasy, w której będą dostępne poszczególne komunikaty - kultura to kod opisujący język (np. pl, en, en-us itp.) - w widokach napisy dostępne są jako namespace.nazwa.napis, gdzie namespace to przestrzeń nazw naszej aplikacji, nazwa to nazwa pliku zasobów zaś napis to nazwa konkretnego napisu, np. <h2>@mvcapplication1.komunikaty.title</h2> - konkretny napis wybierany jest na podstawie pól CurrentCulture i CurrentUICulture bieżącego wątku i trzeba je ustawiać dla każdego zapytania, - można w tym celu wykorzystać metodę Application_AcquireRequestState (w pliku Global.asax.cs, która jest wykonywana dla każdego zapytania: using System.Globalization; public class MyApplication: System.Web.HttpApplication protected void Application_AcquireRequestState(object sender, EventArgs e) string lang = "pl"; // domyślna wartość if(httpcontext.current.request.userlanguages!= null && HttpContext.Current.Request.UserLanguages.Length > 0) lang = HttpContext.Current.Request.UserLanguages[0].Substring(0, 2); CultureInfo ci = new CultureInfo(lang); System.Threading.Thread.CurrentThread.CurrentUICulture = ci; System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name); - można także do pliku web.config, w sekcji system.web dodać wpis: <globalization culture="auto" uiculture="auto" /> co spowoduje automatyczne odczytywanie informacji o języku z nagłówka zapytania - komunikaty o błędach modelu lokalizujemy podając zamiast ErrorMessage parametry ErrorMessageResourceName=nazwa_napisu oraz ErrorMessageResourceType=typ_klasy_zasobu, np.: [StringLength(10, MinimumLength=2, ErrorMessageResourceName="title", ErrorMessageResourceType= typeof(mvcapplication1.resources.mesgs))] - więcej o lokalizacji: http://www.codeproject.com/kb/aspnet/routinglocalizedmvc.aspx http://www.eworldui.net/blog/post/2008/05/aspnet-mvc---localization.aspx Buforowanie widoków: OutputCache - atrybut ten służy do kontrolowania buforowania widoków 662BD5F6-0D63-4080-8769-62B3BA33A4506662BD5F6-0D63-4080-8769-62B3BA33A450
- można go nadać kontrolerowi (wtedy dotyczy wszystkich akcji) jak i poszczególnym akcjom - parametry (wybrane): - Duration czas pamiętania widoku w sekundach - Location miejsce, gdzie pamiętany jest widok (OutputCacheLocation.XXX): Any gdziekolwiek Client po stronie kienta Server po stronie serwera ServerAndClient u klienta i na serwerze None nigdzie - NoStore informuje proxy, że nie powinny przechowywać kopii strony (wartość logiczna) - VaryByParam umożliwia buforowanie widoków różniących się wartościami parametrów: none oznacza, że zawartość strony nie zależy od parametrów * (gwiazdka) oznacza, że każdy zestaw parametrów powinien być oddzielnie buforowany nazwa_parametru oznacza, że dla różnych wartości parametru nazwa_parametru powinna być pamiętana oddzielna kopia strony w pamięci podręcznej p1; p2; p3 jeżeli kilka parametrów wyznacza rozróżnia strony, można rozdzielić je średnikami 662BD5F6-0D63-4080-8769-62B3BA33A4507662BD5F6-0D63-4080-8769-62B3BA33A450