Hello AngularJS

Planując pracę nad aplikacją, założyłem sobie cel zapoznania się z frameworkiem AngularJS. W końcu przyszedł czas przyjrzenie się tej bibliotece. Zaczniemy słowem wstępu. Z czym to jeść?

Angular jest to framework napisany w języku JavaScript i wykorzystuje on HTML. Dzięki niemu możliwe jest tworzenie dynamicznych i łatwych w utrzymaniu (wzorzec MVC) aplikacji internetowych działających po stronie klienta.
Ok wszystko super, ale to na pewno jest ciężkie do ogarnięcia, pomyślicie. Otóż nie, zobaczcie jakie to proste. Poniżej znajduje się kod pierwszej aplikacji. Nie trzeba pobierać żadnych bibliotek. Wystarczy napisać kawałek kodu w notatniku i działa. Magic 😀

Przeanalizujmy kod od początku. Jak widać wygląda jak zwykły HTML, ale coś tu dodano dodatkowo. W oczy rzuca się dyrektywa ng-app, jest ona dodana po to, aby określić jaką część drzewa DOM będzie zarządzał AngularJS. W tym przypadku Angular będzie zarządzał całością, ale nic nie stoi na przeszkodzie aby był to np. tylko jeden <div>.
Następnie mamy kontrolkę input z atrybutem ng-model, chodzi o to aby powiedzieć Angularowi, że to jest nasz model o nazwie input.
Kolejna linijka kodu zawiera takie dziwne znaki {{ input }}, to właśnie tutaj Angular wstrzyknie nową wartość, odświeży ją dynamicznie. W tym przypadku wartość z pola input.
Na końcu sekcji body określamy skrypt Angularowy, lepiej określić go w ten sposób, aby klient nie musiał go za każdym razem pobierać. Skrypt będzie przechowywany w pamięci cache przeglądarki, który jest dostępny dla wielu aplikacji.
To na tyle słowem wstępu do Angulara. W kolejnym wpisie/wpisach przyjrzymy się bliżej tej bibliotece.

Deploy web aplikacji do chmury

Jeżeli przy tworzeniu nowego projektu w Visual Studio nie zaznaczyliśmy opcji „Host in the cloud”, ale chcemy opublikować naszą aplikacje w chmurze Azure, to ten krótki poradnik może okazać się przydatny. Pierwszą rzeczą jaką musimy zrobić jest stworzenie web aplikacji w Azure.
Wybieramy Usługi aplikacji z portalu i klikamy dodaj

clip_image001

Po stworzeniu aplikacji, możemy pobrać profil jej ustawień klikając „Pobierz profil…”

clip_image002

Otwieramy solucję w Visual Studio, wybieramy projekt do deployu i wybieramy Publish, a następnie Import i wybieramy plik z profilem *.PublishSettings pobrany z portalu Azure.

clip_image003

clip_image004

clip_image005

Dla pewności możemy wykonać test połączenia oraz upragniony Publish. That’s it. Nasza aplikacja już działa na Azure, przynajmniej powinna, o ile stworzyliśmy wcześniej także bazę danych i zmieniliśmy odpowiednio ConnectionStringa w web.configu.

Logowanie za pomocą konta google

userLogin

W aplikacji ASP.NET MVC mamy możliwość rejestracji kont użytkowników, jednak co w przypadku gdy chcielibyśmy dać użytkownikom możliwość logowania kontami zewnętrznymi, takimi jak google, facebook, itp? Aby umożliwić logowanie kontami zewnętrznymi, musimy przede wszystkim zajrzeć do pliku Startup.Auth.cs i w metodzie ConfigureAuth odkomentować odpowiednie linie kodu.

Jak widać możemy użyć kont Microsoft, Twitter, Facebook oraz Google. W tym poście skupimy się na logowaniu za pomocą konta Google. Tak więc musimy odkomentować ostatnią opcję:

Niezbędne będzie podanie ClientId i ClientSecret, aby je uzyskać udajemy się pod adres: https://console.developers.google.com i tworzymy nowy projekt. Następnie przechodzimy do zakładki Credentials i wybieramy Create creadentials określając nazwę naszej aplikacji, rodzaj, itd. Należy wpisać także odpowiednie redirect Url. Authorized JavaScript origins: http://localhost:portNumber oraz Authorized redirect URls: http://localhost:portNumber/signin-google . Zamiast localhost:portNumber podajemy adres naszej aplikacji. Jeżeli uruchamiamy ją na localhoście to adres powinien tak właśnie wyglądać, tylko trzeba wpisać odpowiedni port z jakiego korzysta aplikacja. Oprócz tego musimy jeszcze aktywować Google+ API, w tym celu przechodzimy do naszego projektu i z głównego ekranu wszystkich dostępnych API wybieramy Google+ API i klikamy enable. Po przeklikaniu się przez kilka kroków dostaniemy wygenerowane klucze ClientId i ClientSecret, które należy skopiować do powyższej metody. Po uruchomieniu aplikacji naszym oczom ukaże się dodatkowa opcja logowania.

clip_image001

clip_image002

I w ten oto sposób użytkownicy będą mogli logować się do naszej aplikacji kontami google.

Atrybuty walidacji w modelu

Projektując formularze, chcielibyśmy aby użytkownik podał w polach odpowiednie dane. Co zrobić aby sprawdzić poprawność wpisywanych danych? Z pomocą przychodzą nam atrybuty walidacji, które udekorują właściwości naszego modelu. Pisząc o walidacji, trzeba wspomnieć że możliwości są dwie: walidacja po stronie klienta oraz walidacja po stronie serwera. Dobrze jest skorzystać z obu opcji i walidować dane już po stronie klienta, aby nie przesyłać żądań do serwera dla każdego pola. Aby aktywować walidację po stronie klienta w aplikacji ASP.NET MVC należy upewnić się czy w pliku web.config, w sekcji appSettings widnieje taki oto wpis:

Dzięki temu oraz atrybutom w modelu uzyskamy walidację w obu przypadkach. Jakie atrybuty mamy do dyspozycji? Poniżej przedstawiam listę, mam nadzieję że żadnego nie pominąłem 😉

[Required(ErrorMessage = „This field is required”)]

[EmailAddress(ErrorMessage=”Incorrect e-mail address”)]

[Range(1, 99, ErrorMessage = „Incorrect value, please type value 1-99″)]

[RegularExpression(@”^[A-Z]+[a-zA-Z’’-‘\s]*$”)]

[Compare(„NewValue”, ErrorMessage = „Values are not the same”)]

[DataType(DataType.Time)]

[StringLength(256, MinimumLength=3)]

[MaxLength(500)]

Przykład:
Mamy w modelu właściwość:

Spróbujmy teraz wpisać w to pole wartość z poza podanego zakresu

clip_image001

Jak widać walidacja zadziałała, bez wysyłania żądania do serwera. W przypadku wyłączonego wykonywania JavaScript w przeglądarce użytkownika, walidacja po stronie klienta nie zadziała, ale walidacja po stronie serwera się wykona.

ViewBag, ViewData, TempData

Gdy już jesteśmy w temacie kontrolerów, warto także poruszyć temat przekazywania danych do widoków. Oczywiście robimy to za pomocą ViewModeli, ale istnieją także inne metody przekazywana danych. Do dyspozycji mamy takie obiekty jak:

  • ViewBag,
  • ViewData,
  • TempData.

ViewBag i ViewData są obiektami tworzonymi tylko na czas bieżącego żądania. Podczas przekierowania pod inny adres wartości w nich zawarte przyjmują wartość null. ViewData jest to słownik, którego kluczem jest string, a wartością typ object. Podczas używania wymagane jest sprawdzanie przed nullem i rzutowanie, aby uniknąć pojawienia się błędu.
ViewBag jest typu dynamic, więc może przyjąć dowolny typ. Nie wymaga rzutowania typów.
Nieco więcej różnic zawiera TempData. Pozawala on na przekazywanie danych pomiędzy dwoma żądaniami HTTP (bieżącym i kolejnym), przechowuje on dane przez cały czas trwania żądania HTTP. Pomaga utrzymać dane podczas przemieszczania się pomiędzy kontrolerami lub akcjami. Korzystając z TempData musimy zapewnić sprawdzenie wartości przed nullem oraz rzutowanie na odpowiedni typ, w celu uniknięcia wystąpienia błędu. Obiekt ten jest używany głównie do przechowywania jednorazowych wiadomości jak np. komunikaty błędów.
Przykład: chcemy przekazać wartości lokalizacji i typów do kontrolki DropDown w widoku.
Kod zawarty w kontrolerze:

W obu przypadkach z ViewBag i ViewData wartości zostaną poprawnie przekazane do widoku.
Kod zawarty w widoku:

Możemy też bezpośrednio dobrać się do wartości zawartych w obiektach ViewBag i ViewData:

W przypadku TempData wygląda to tak samo.
Kod w kontrolerze:

Kod w widoku:

W przypadku przekazywania wartości TempData pomiędzy akcjami wygląda to tak:

Zapisujemy wartość do obiektu TempData w akcji index i przekierowujemy do akcji Create.
W akcji create pobieramy wartość TempData[„TestVal”] i sprawdzamy czy jest to „TempDataTestVal”.

clip_image001

Jak widać wartość została przekazana.

Routing w ASP.NET MVC

clip_image001

Routing jest to pewnego rodzaju wzór adresu URL, który mapowany jest do odpowiedniej akcji kontrolera lub po prostu do pliku który ma zostać zwrócony. Konfiguracja routingu znajduje się w klasie RouteConfig (w folderze App_Start), w statycznej metodzie RegisterRoutes. Metoda ta wywoływana jest w metodzie Aplication_Start() w klasie MvcApplication z pliku Global.asax.cs. Domyślnie w nowo stworzonym projekcie ASP.NET MVC klasa RouteConfig wygląda tak:

W ciele metody RegisterRoutes widzimy wywołanie metody IngoreRoute. Dzięki temu do listy ignorowanych ścieżek zostanie dodana ścieżka wskazująca na jakikolwiek zasób z rozszerzeniem .xsd. Metoda MapRoute, definiuje nam regułę routingu. Należy pamiętać o kolejności definiowanych zasad routingu, ponieważ działa tutaj reguła pierwszego dopasowania, pierwsza pasująca reguła zostanie użyta.
W ASP.NET MVC 5 dodatkowo wprowadzone zostały atrybuty routingu. W celu uruchomienia routingu na podstawie atrybutów należy dodać wywołanie Metody MapMvcAttribytesRoutes w metodzie RegisterRoutes:

Atrybuty routingu pozwalają na dekorowanie akcji atrybutem Route z określeniem zasady routingu:

Dodatkowo atrybuty mogą zawierać ograniczenia, np. :

Gdzie X jest ograniczeniem: datetime, decimal, double, float, guid, int, length(n), long, max(n), maxlength(n), min(n), minlength(n), range(n,m), regex(someRegExp).

Pisząc o routingu, należy także wspomnieć o możliwości definiowania obszarów. Kiedy chcemy użyć w naszej aplikacji kilku kontrolerów o takiej samej nazwie (np. jeden do panelu admina, a drugi dla pozostałych użytkowników), przydatne mogą się okazać obszary – RouteArea. Definiując obszar, wyeliminujemy problem wyboru odpowiedniego kontrolera.

Wtedy link do akcji kontrolera z uwzględnieniem obszaru prezentuje się następująco:

Kontroler i filtry akcji

spa-filtersW poprzednim wpisie przyjrzeliśmy się kontrolerom oraz ich akcjom, natomiast teraz rzucimy okiem na filtry, czyli atrybuty, które można zastosować zarówno dla każdej z akcji jak i dla całego kontrolera. Atrybuty definiują nam sposób wykonania określonych akcji. W ASP.NET MVC mamy do dyspozycji kilka typów filtrów akcji:

  • Filtry autoryzacji – są wywoływane na samym początku, przed pozostałymi filtrami i akcją.
    Decydują o możliwości wykonania metody po wcześniejszej autentykacji lub walidacji właściwości danego żądania. Implementują interfejs IAutorizationFilter zawierający metodę OnAuthorization.
  • Filtry akcji – zapewniają wykonanie dodatkowych operacji, zarówno przed jak i po wykonaniu akcji. Implementują interfejs IActionFilter, który zawiera dwie metody: OnActionExecuting i OnActionExecuted. Pierwsza z nich wykonuje się przed daną akcją, natomiast druga wykonuje się po zakończeniu akcji.
  • Filtry rezultatu – opakowują wywołanie obiektu typu ActionResult. Implementują interfejs IResultFilter, który zawiera dwie metody podobnie jak interfejs dla filtrów akcji. Jedna z nich to OnResultExecuting i wykonuje się przed zwróceniem rezultatu, a druga to OnResultExecuted i wykonuje się po zwróceniu rezultatu.
  • Filtry wyjątków – wykonują się gdy nastąpi nieobsłużony wyjątek gdzieś w akcji. Implementują interfejs IExceptionFilter zawierający metodę OnException.

Teraz zobaczmy jak można z tych filtrów skorzystać. Użyjemy najpierw filtra autoryzacji. Chcemy wykonać akcję tylko w momencie gdy użytkownik będzie zautoryzowany. W tym celu dekorujemy naszą metodę atrybutem [Authorize] i to wszystko.

Kolejnym zaimplementowanym filtrem, którego możemy użyć jest [HandleError], który określa sposób łapania wyjątków.

W przypadku gdy zechcemy wymusić korzystanie z połączenia po HTTPS wystarczy użyć atrybutu [RequireHttps]

Kolejny ważniejszy atrybut to [ValidateAntiForgeryToken], o nim była mowa w jednym z poprzednich postów o CSRF. Za jego pomocą sprawdzane są klucze (ten wygenerowany w formularzu z kluczem przesłanym w żądaniu).

Nic nie stoi na przeszkodzie, aby dodawać kilka filtrów do jednej akcji. Możemy również tworzyć własne filtry, implementując odpowiednie interfejsy.
Wspomniałem na początku, że filtry można zastosować nie tylko do konkretnych metod kontrolera, ale też do całej klasy. Można to zrobić np. w ten sposób:

Dzięki temu, każda akcja wymaga autoryzacji do wykonania, bez konieczności powielania atrybutu dla każdej akcji.

Oprócz wyżej wymienionych, mamy do dyspozycji jeszcze kilka innych ciekawych atrybutów takich jak:

  • ValidateInput – walidacja danych wejściowych
  • HttpGet – obsługa żądania GET, nie trzeba dodawać tego atrybutu, ponieważ domyślnie metoda obsługuje żądania GET
  • HttpPost – obsługa żądania POST
  • OutputCache – powoduje cachowanie metody, możliwe jest określenie czasu przechowywania w cachu

 

Kontroler i Akcje

W tym wpisie przyjrzymy się kontrolerom w aplikacji ASP.NET MVC oraz akcjom, które mogą one zawierać. Kontrolery są odpowiedzialne za odpowiadanie na żądania użytkownika. Każde żądanie kierowane z przeglądarki jest obsługiwane przez określony kontroler. Przykładowo użytkownik klika przycisk odpowiadający za dodanie nowego wydarzenia, natomiast kontroler (EventsController) w odpowiedzi wyświetla odpowiedni widok. Akcja, która się w tym przypadku wykona jest zawarta w metodzie Create typu GET.

Zgodnie z dobrymi praktykami należy pamiętać, że w kontrolerze nie powinno być zawartej logiki biznesowej, ani logiki odpowiedzialnej za dostęp do danych. No dobrze, ale jak to się dzieje że akcje są wykonywane przez odpowiednie metody kontrolera? Za to odpowiedzialny jest routing. Przykładowo wywołujemy URL: http://localhost/Events/Create i w tym przypadku zostanie wywołana metoda Create() z klasy EventsController. Akcje kontrolera muszą być metodami publicznymi. Także każda metoda publiczna w kontrolerze staje się wystawioną na zewnątrz akcją. Każda akcja zwraca typ ActionResult. W przypadku powyższej metody Create zwracany jest typ ViewResult. Wystarczy zwrócić wynik metody View(), a zwrócony zostanie widok o nazwie Create znajdujący się w katalogu Events (czyli o takiej samej nazwie jak kontroler). Identyczny efekt osiągniemy gdy wywołamy metodę View(„Create”), wtedy jawnie wskazujemy nazwę widoku. Poza typem ViewResult w ASP.NET MVC jest dostępnych jeszcze kilka innych typów. Lista wszystkich typów prezentuje się następująco:

  • ViewResult – zwraca widok
  • PartialViewResult – zwraca widok częściowy
  • EmptyResult – zwraca null
  • RedirectResult – przekierowuje do innej akcji za pomocą adresu URL
  • RedirectToRouteResult – przekierowuje do innej akcji bez odwiedzania adresu URL
  • JsonResult – zwraca dane w postaci JSON
  • JavaScriptResult – zwraca kod w postaci JavaScript
  • ContentResult – zwraca wartość tekstową
  • FileContentResult – reprezentuje plik do pobrania, jako tablicę bitów
  • FilePathResult – zwraca ścieżkę do pliku
  • FileStreamResult – reprezentuje plik do pobrania, jako strumień danych

Każdy z typów dziedziczy z klasy bazowej ActionResult. Poniżej znajdują się przykładowe implementacje akcji:

 

Zabezpieczenie przed atakami CSRF w ASP.NET MVC

image

CSRF czyli Cross-Site Request Forgery jest to w skrócie zmuszenie przeglądarki ofiary do wykonania nieautoryzowanej akcji (za pomocą żądania HTTP). Ofiarą jest zalogowany do serwisu użytkownik. Celem takiego ataku może być podmiana danych w formularzu lub też wykonanie innej akcji np. stworzenie konta użytkownika z uprawnieniami admina. Wypadałoby się przed tym zabezpieczyć tworząc aplikacje webową. Na szczęście mechanizm zabezpieczeń otrzymujemy w ASP.NET MVC out of the box :)
Pokażmy to na przykładzie. Mamy kontroler eventów, który zawiera metody Edit – Get i Post.

Teraz wystarczy, że haker skłoni nas do wyświetlenia strony zawierającej formularz z auto submitem, przez niego spreparowany z wpisanymi wartościami, np. strona HTML może zawierać taki kawałek kodu:

W przykładzie tym została podmieniona data rozpoczęcia eventu. Nie jest to co prawda jakaś bardzo inwazyjna operacja, ale obrazuje działanie CSRF. W przypadku braku zabezpieczeń, data wybranego wydarzenia zostanie zaktualizowana. Z pomocą przychodzi nam atrybut [ValidateAntiForgeryToken], którym możemy udekorować naszą metodę Edit.

Ponadto w widoku musi się znaleźć taki fragment kodu:

powoduje to wygenerowanie ciasteczka i klucza, który jest ukryty w formie i wygląda tak:

W żądaniu POST ciasteczko jest przesyłane i za pomocą atrybutu [ValidateAntiForgeryToken] sprawdzana jest zgodność klucza. Gdy klucz nie będzie zgodny, wystąpi błąd. Dodatkowo w metodzie Edit można dodać jeszcze jedno zabezpieczenie, chodzi o binding.

Dzięki temu określamy jakie dane mogą zostać zapisane, ponadto w żądaniu sprawdzane są typy i wartości. Poprawność danych wskaże nam właściwość ModelState.IsValid.

Podsumowując, nie należy ignorować podatności CSRF i odpowiednio się przed nią zabezpieczyć. Na szczęście ASP.NET MVC robi to za nas :)

DateTime w widoku edycji

Dzisiejszy wpis poświęcony będzie typowi DateTime. Wyświetlając widok edycji zdarzenia, natrafiłem na taki oto problem. Pomimo tego, że zdarzenie ma zapisaną datę, co potwierdza metoda Edit typu GET, więc data powinna zostać załadowane do widoku, tak się jednak nie dzieje.

clip_image001

W widoku zamiast daty rozpoczęcia, widzimy dd.mm.rrrr, dodatkowo możemy wybrać jakąś datę z date pickera (działa tylko w Chrome).

clip_image002

Ten sam przykład w przeglądarce Internet Explorer

clip_image003

W IE nie ma date pickera ponieważ IE nie wspiera typu date, ale data wyświetlana jest prawidłowo. No dobrze to przyjrzyjmy się w takim razie naszemu modelowi. Kod właściwości Data rozpoczęcia prezentuje się tak:

Kluczowym elementem jest tutaj atrybut DisplayFormat. Aby rozwiązać problem wystarczy zmienić format wyświetlanej daty na yyyy-MM-dd, tylko w taki sposób przeglądarka sobie z tym poradzi. Poprawiony kod właściwości Data rozpoczęcia:

Taka sama sytuacja ma miejsce w przypadku time pickerów.

clip_image004

Kod w modelu dla właściwości Czas zakończenia wygląda następująco.

Po zmianie formatu na hh:mm:ss, problem zostaje rozwiązany. Poniżej kod po poprawkach.

Jak widać format ma znaczenie 😉