Author Archives: Jakub Skoczeń

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 😉

Do czego służy plik Global.asax?

W aplikacji webowej ASP.NET MVC możemy znaleźć plik Global.asax zawierający klasę MvcApplication dziedziczącą po klasie HttpApplication. Postanowiłem poświęcić temu kolejny wpis, ponieważ myślę że warto opisać do czego on służy, tym bardziej że pytanie to może być także zadane na rozmowach o pracę 😉

globalPlik Global.asax jest to opcjonalny plik zawierający kod, który odpowiada za reagowanie na zdarzenia zgłoszone z poziomu aplikacji i poziomu sesji takie jak start aplikacji, zakończenie sesji, itp. Plik ten jest opcjonalny, czyli nie musimy reagować na te zdarzenia, jeżeli nie chcemy.
Domyślnie plik ten zawiera następujący kod:

Jak widać jest tutaj tylko zaimplementowane obsłużenie zdarzenia startu aplikacji, jednak nic nie stoi na przeszkodzie, aby dodać obsługę innych zdarzeń.
W tym celu wystarczy dodać odpowiednio nazwaną metodę/metody np.

To wszystko, nie musimy robić nic więcej (poza implementacją reakcji na zdarzenie). Gdy zdarzenie zostanie zgłoszone, zostanie ono odpowiednio obsłużone.
Lista zdarzeń wraz z krótkim opisem każdego z nich prezentuje się następująco:

Application_Init: pojawia się podczas inicjalizacji aplikacji albo pierwszego wywołania.
Application_Disposed: pojawia się zaraz przed końcem życia aplikacji. Idealne miejsce do zwolnienia używanych zasobów.
Application_Error: pojawia się podczas wystąpienia błędu, który nie został obsłużony w aplikacji.
Application_Start: pojawia się podczas stworzenia pierwszej instancji klasy HttpApplication. Pozwala na stworzenie obiektów dostępnych we wszystkich instancjach HttpApplication.
Application_End: pojawia się, kiedy ostatnia instancja klasy HttpApplication kończy swój cykl życia. Odpalany tylko raz.
Application_BeginRequest: pojawia się podczas odebrania żądania aplikacji. Jest to pierwsze odpalane zdarzenie.
Application_EndRequest: jest to ostatnie zdarzenie, które pojawia się podczas żądania aplikacji.
Application_PreRequestHandlerExecute: pojawia się przed wykonaniem obsługi zdarzenia strony albo web serwisu.
Application_PostRequestHandlerExecute: pojawia się na koniec wykonania obsługi zdarzenia.
Applcation_PreSendRequestHeaders: pojawia się przed wysłaniem nagłówków HTTP do klienta wysyłającego żądanie (przeglądarka).
Application_PreSendRequestContent: pojawia się przed wysłaniem zawartości do klienta wysyłającego żądanie (przeglądarka).
Application_AcquireRequestState: pojawia się podczas pobrania bieżącego stanu (stanu sesji) związanego z bieżącym żądaniem.
Application_ReleaseRequestState: pojawia się podczas zakończenia wykonania obsługi wszystkich zdarzeń.
Application_ResolveRequestCache: pojawia się, kiedy zakończy się autoryzacja, by pozwolić modułom cachującym obsługiwać żądania z cachu obchodząc wykonania obsługi zdarzenia np. strony lub web serwisu.
Application_UpdateRequestCache: pojawia się po zakończeniu wykonania obsługi zdarzenia, by pozwolić modułom cachującym przechowywać odpowiedzi do obsługi kolejnych żądań.
Application_AuthenticateRequest: pojawia się, gdy moduł zabezpieczeń ustali tożsamość bieżącego użytkownika. Od tego momentu poświadczenia użytkownika są zatwierdzone.
Application_AuthorizeRequest: pojawia się, gdy moduł zabezpieczeń zweryfikuje, że użytkownik może uzyskać dostęp do zasobów.
Application_MapRequestHandler: pojawia się, gdy procedura obsługi jest wybrana do odpowiedzi na żądanie.
Application_PostLogRequest: pojawia się podczas zakończenia przetwarzania obsługi wszystkich zdarzeń dla zdarzenia LogRequest
Session_Start: pojawia się, gdy nowy użytkownik odwiedzi stronę aplikacji.
Session_End: pojawia się, gdy sesja użytkownika przekroczy limit czasu, zakończy się albo strona aplikacji zostanie opuszczona.

Warto o tym wiedzieć, bo być może kiedyś przyda nam się obsługa któregoś ze zdarzeń.

Dlaczego nie zwracać typu IQueryable w repo?

clip_image001W ostatnim wpisie poruszyłem tematykę IoC i w przykładowym kawałku kodu dla LocationRepo znajduje się metoda GetLocations zwracająca typ IQueryable. Zwróciła na to uwagę pewna osoba odwiedzająca bloga i słusznie. Stwierdziłem więc, że lepiej będzie rozpisać się krótko na ten temat niż edytować poprzedni wpis. No więc dlaczego zwracanie typu IQueryable<T> jest złe? Można by powiedzieć, że zwracanie ogólnie jest złe i czasem się zdarza 😉 ale do rzeczy.

Implementując repozytorium powinniśmy tworzyć nową metodę dla każdego zapytania, np. GetLocationsByName, GetLocationsBySth, itd. Nie jest dobrą praktyką utworzenie jednej metody albo właściwości zwracającej całą kolekcję typu IQueryable<T>, po to by następnie filtrować elementy przy pomocy LINQ już poza repozytorium. Zapytania LINQ stosowane dla kolekcji IQueryable<T> są tłumaczone na drzewa wyrażeń, które następnie tłumaczone są na język określonego źródła danych, np. SQL. Zwracając z repozytorium taki typ, odpowiedzialność za tworzenie zapytań do źródła danych przechodzi na dewelopera implementującego kod w warstwie wyższej, warstwie domeny. W konsekwencji kod taki nie jest testowalny w 100%. Nie znamy wszystkich możliwych kombinacji zapytań, które mogą występować.
Kolejnym argumentem, który przemawia za niestosowaniem typu IQueryable w repozytoriach, jest to że uzależniamy nasze repozytorium od konieczności używania Entity Frameworka albo LINQ to SQL, ponieważ w przypadku innych źródeł danych musiałby być zaimplementowany mechanizm zwracania IQueryable. Chcemy aby nasz kod był uniwersalny i nie chcemy utrudniać sobie życia implementując dla każdego nowego źródła danych IQueryable, więc jedynym wyjściem jest unikanie tego typu. Oczywiście czasami może się on przydać, jednak musimy mieć to wszystko na uwadze 😉
Poniżej przykład metod zawartych w repozytorium.
Tak nie robimy

lepiej zróbmy to tak

Jak dodać kontener IoC?

Doctor with a syringe and patient. Isolated over white

Co to jest kontener IoC? IoC czyli Inversion of Control lub też odwrócenie zależności, brzmi bardzo ogólnie, ale co się pod tym kryje?
Możemy tutaj zaliczyć wzorzec fabryki, service locatora (lub też antywzorzec), czy też dependency injection i na tym ostatnim się skupimy.
Wzorzec ten może zostać zaimplementowany np. poprzez: constructor injection, property injection, jednak najbardziej sensowny jest ten pierwszy sposób.
Polega on na wstrzyknięciu zależności poprzez konstruktor. Kontener IoC ma nam w tym wszystkim pomóc, chodzi o to aby wstrzyknął on za nas jakiś konkretny typ, np. za typ ILocationRepo zostanie wstrzyknięty LocationRepo.

Po co nam to panie? No to zacznijmy od początku…
W przypadku web aplikacji ASP.NET MVC, domyślnie stworzony kontroler zawiera w sobie logikę, jednak dobrą praktyką jest wydzielenie warstwy logiki. W tym celu stworzyłem repozytoria w osobnym projekcie. Jednak aby wykorzystać dane repozytorium w kontrolerze, nie chcemy tworzyć instancji konkretnej klasy, chcemy być bardziej elastyczni i w razie potrzeby wstrzyknąć w to miejsce nową implementacje repozytorium. Do tego celu zastosujemy Dependency Injection i kontener IoC.

Pierwsza rzecz, która pozostaje do zrobienia to implementacja interfejsów dla każdego repozytorium oraz dla kontekstu. Tworzymy przykładowo interfejs dla repozytorium Location. Na razie zawiera on tylko metodę GetLocations(), jednak później będzie on zawierał więcej metod.

Implementujemy ten interfejs w klasie LocationRepo.

Widzimy tutaj także typ ITimePlannerContext – jest to interfejs naszego kontekstu, który także należy stworzyć, aby nie uzależniać się od konkretnego kontekstu w repozytoriach. Następnie możemy już przejść do kontrolera i stworzyć w nim konstruktor, który umożliwi nam constructor injection.

Ok, ale skąd wiadomo jaką implementację wstrzyknąć pod dany interfejs? Do tego posłuży nam kontener IoC.

Jak dodać kontener IoC?
Poniżej przedstawię użycie kontenera Unity, jednak rozpocząłem też zabawę z Autofac i chyba w późniejszym czasie skorzystam z niego, ale o tym może później. Gdyby ktoś był ciekawy jak wypadają inne kontenery w porównaniu, szukając w sieci trafiłem na ciekawy link.
Teraz czas przedstawić jak zastosować Unity. Otwieramy NuGet package managera i wyszukujemy Unity.Mvc, instalujemy w naszej web aplikacji.

clip_image001

clip_image002

Po zainstalowaniu paczki, do folderu App_Start w naszej aplikacji zostaną dodane dwie dodatkowe klasy: UnityConfig i UnityMvcActivator. Przechodzimy do klasy UnityConfig i przystępujemy do implementacji metody RegisterTypes, w której zdefiniujemy które klasy implementują określone interfejsy. Kod całej klasy wygląda następująco.

W metodzie RegisterTypes jak sama nazwa wskazuje, rejestrowane są typy i tak np. za typ ILocationRepo podstawiamy LocationRepo, itd. W przypadku gdy stworzymy nową wersję LocationRepo i będziemy chcieli z niej korzystać, to wystarczy tylko ją tutaj zarejestrować i to wszystko. Oczywiście gdyby ktoś chciał można też wykorzystać w tym miejscu refleksję i wtedy zwolni nas to z obowiązku rejestrowania każdego nowego typu. Teraz możemy uruchomić aplikację i sprawdzić efekt. Bardzo proste prawda? Za to ile korzyści 😉

Do czego służy metoda AsNoTracking()?

Korzystając z Entity Frameworka warto zwracać uwagę na sposób pobierania danych.
W przypadku, gdy nie będziemy ich modyfikować, a chcemy jedynie pobrać dane tylko do odczytu, przydatna okaże się metoda AsNoTracking(). Wywołanie metody skutkuje brakiem śledzenia danych przez kontekst. Dzięki temu nie marnujemy niepotrzebnie zasobów.
Przyjrzyjmy się poniższemu przykładowi, w którym porównamy pobieranie danych z i bez metody AsNoTracking(). Na początek pobieramy dane standardowo. W tym celu modyfikujemy metodę Index() kontrolera LocationsController i debugując przechodzimy za linię kodu odpowiadającą za zwrócenie danych do widoku. W okienku watch podglądamy nasz obiekt kontekstu i widzimy, że 4 lokalizacje zostały do niego zapisane.

clip_image001

Teraz czas na wywołanie metody AsNoTracking() i upewnienie się, że dane nie są zapisywane w kontekście. Tym razem metoda Index() prezentuje się następująco:

Debugujemy, przechodzimy za ostatnią linię kodu w metodzie Index() i podglądamy nasz obiekt kontekstu.

clip_image002

Jak widać lokalizacje nie zostały tym razem zapisane do kontekstu, nie są przez niego śledzone. W tym przypadku oszczędności pamięci są niewielkie, ale wyobraźmy sobie gdybyśmy mieli takich lokalizacji tysiące, spowodowałoby to niepotrzebne straty. Dlatego należy zwrócić szczególną uwagę w takich sytuacjach i stosować AsNoTracking(), tam gdzie tylko jest to możliwe.

Exception has been thrown by the target of an invocation

Dodając kontroler – MVC 5 Controller with views, using Entity Framework. Możemy napotkać pewien problem.

clip_image001

Po kliknięciu Add pojawia się błąd: Exception has been thrown by the target of an invocation.

clip_image002

Googlując możemy znaleźć dosyć dużo osób mających taki sam problem, jednak większość proponowanych rozwiązań nie dawała w moim przypadku rezultatu, np. reinstalacja paczki EntityFramework. Niektórzy proponowali aktualizację Visual Studio, jednak tyczy się to tylko VS2013. Próbowałem nawet użyć VS2013, jednak było to samo. Rozwiązanie okazało się bardzo proste, wystarczyło edytować connection stringa w pliku Web.config. Wcześniej zmieniłem domyślną nazwę klasy kontekstu, ale nie wprowadziłem zmian do Web.configa.
Było:

należało zmienić nazwę na dokładnie taką samą jak klasa kontekstu czyli:

Ta da, działa!
Kontroler został utworzony jak również widoki. Błąd jest banalny, ale może ktoś napotka ten sam problem i dzięki temu wpisowi zaoszczędzi kilka minut Puszczam oczko