Integracja Fluent Validation z ASP.NET MVC

Wprowadzenie

W dzisiejszym wpisie pokażę dwa sposoby, dzięki którym można integrować bibliotekę Fluent Validation z ASP.NET MVC. Pierwszy domyślny, który dostarcza sama biblioteka, oraz drugi mój, który rozwiązuje część problemów z domyślnej integracji.

Domyślna integracja Fluent Validation z ASP.NET MVC

Integracja Fluent Validation z ASP.NET MVC jest bardzo prosta i sprowadza się do dwóch kroków:

  • Instalacja pakietu FluentValidation.Mvc5 z nugeta
  • Dodanie linijki konfiguracji biblioteki w metodzie startującej aplikację (MvcApplication.Application_Start w pliku Global.asax.cs):

Mając już zintegrowaną bibliotekę, wystarczy dla klasy (prawdopodobnie będzie to jakiś ViewModel przekazywany jako parametr do akcji POST) dodać walidator i udekorować ją atrybutem Validator, przekazując typ walidatora – tak, jak to jest zrobione poniżej:

Fluent Validation integruje się z mechanizmem walidacji ASP.NET MVC, dlatego kod po stronie kontrolera jest identyczny jak w przypadku walidacji z wykorzystaniem data annotations (ma przy tym dużo większe możliwości). Poniżej przykład akcji:

W wyniku powyższego kodu (oraz widoku create wygenerowanego przez Visual Studio na podstawie view modelu) otrzymamy taki widok przy błędnej walidacji:

Wynik domyślnej integracji Fluent Validation z ASP.NET MVC

Wynik domyślnej integracji Fluent Validation z ASP.NET MVC

Takie podejście jest fajne w momencie, gdy przychodzimy z data annotations na Fluent Validation. Wystarczy wtedy przenieść logikę walidacji z atrybutów i ewentualnie kontrolera do walidatora oraz zostawić większość kodu w samym kontrolerze.

Co fajne, podobnie jak data annotations, Fluent Validation dla podstawowych walidatorów dodaje sprawdzanie danych po stronie javascript.

Niestety takie podejście ma też wady, które skłoniły mnie do poszukania własnego podejścia do integracji. Przede wszystkim walidacja oparta na view modelach powoduje, że bardzo często mamy tę samą logikę biznesową walidacji w różnych walidatorach. O ile dodanie obiektu oraz edycję można zrobić na jednym view modelu, to już edycję (np. użytkownika) w różnych kontrolerach (np. użytkownik edytuje sam swój profil, a drugą akcję wykonuje administrator) realizujemy na ogół na dwóch różnych view modelach zawierających różne pola. W takim scenariuszu potrzebujemy dwóch walidatorów, które zawierają właściwie taką samą logikę.

Dodatkowo bardzo często w aplikacji mamy kilka różnych końcówek (np. widoki html dla użytkownika korzystającego z przeglądarki oraz web api dla aplikacji mobilnych). W takiej sytuacji również walidacja na poziomie view modeli powoduje, że musimy kopiować reguły walidacji.

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 20 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Własna integracja Fluent Validation z ASP.NET MVC

Aby rozwiązać problemy domyślnej integracji, przygotowałem własną, która rozwiązuje powyższe problemy, ale niestety też nie jest bez wad. Na szczęście wady są dużo mniejsze niż zalety.

Przede wszystkim wychodzę z założenia, że walidacja jest elementem logiki biznesowej aplikacji, dlatego powinna być na jej poziomie, a nie na poziomie obsługi żądania.

W moich aplikacjach wszystkie metody biznesowe (i nie tylko one) zwracają obiekt Result, który może wyglądać mniej więcej tak (w realnych aplikacjach jest nieco bardziej rozbudowany, np. ma możliwość przekazywania wyjątków):

Result ma informację o tym, czy operacja wykonała się poprawnie, czy nie, oraz w przypadku błędów walidacji w kolekcji Errors znajduje się informacja o nich.

Przy takim podejściu walidację robimy na podstawie obiektu domenowego, w przykładzie klasy User:

Następnie sama walidacja przebiega „ręcznie” w metodzie w logice biznesowej, gdzie zwracany jest właśnie obiekt Result (w tym przypadku wersja generyczna zwracająca zapisanego użytkownika):

Użycie własnej implementacji

Za pomocą Extensions Method opakowuję następnie kawałek kodu, który przekopiowuje informacje o błędach do ModelState, aby już samo ASP.NET MVC wyświetliło informacje o błędach w widoku. Wykorzystuję Extensions Method w projekcie webowym, ponieważ Result jest zdefiniowany na poziomie logiki biznesowej, w której nie chcę referować do ASP.NET MVC. Kod jest dość prosty:

Następnie pozostaje użyć logiki biznesowej w kontrolerze i po sprawdzeniu wyniku operacji skopiować błędy do ModelState:

Jak widać na powyższym kodzie, w akcji dla POST w pierwszej kolejności sprawdzam również ModelState. To sprawdzenie robię na potrzeby weryfikacji poprawności wpisanych danych dla ich typów. Gdy na przykład oczekujemy w polu inta, a użytkownik wpisze coś, co się nie parsuje, zostanie to wyłapane przez tę linijkę. Gdyby tak nie było, wtedy właściwość w view modelu będzie miała wartość domyślną (w tym przypadku 0), która może oznaczać wartość poprawną.

W drugim kroku mapujemy view model na model. W normalnej sytuacji używam do tego automappera, ale w przykładzie zrobiłem to ręcznie, aby za bardzo tego przykładu nie rozbudowywać. Następnie wywołujemy metodę logiki biznesowej i sprawdzamy, czy operacja się udała. Jeśli nie, za pomocą wcześniej dodanej metody przerzucamy błędy z obiektu wyniku do ModelState i wyświetlamy widok ponownie.

Wynik samego działa aplikacji jest taki sam, jak w domyślnej integracji:

Wynik własnej integracji Fluent Validation z ASP.NET MVC

Wynik własnej integracji Fluent Validation z ASP.NET MVC

Podsumowanie

Jak widać wyżej, taka integracja powoduje, że możemy współdzielić logikę walidacji między różnymi akcjami w aplikacji czy nawet między różnymi punktami dostępowymi – w ASP.NET MVC oraz ASP.NET WEB API używamy tych samych metod logiki biznesowej.

Niestety to rozwiązanie nie jest idealne. W kodzie kontrolerów musimy za każdym razem napisać kilka linijek więcej kodu, a po stronie javascript w tym podejściu nie mamy walidacji dla podstawowych walidatorów. W drugim przypadku zauważyłem, że na ogół dodajemy ręcznie walidację w javascript, więc oba te minusy w moim przypadku są dużo mniejsze niż konieczność powielania reguł walidacji dla różnych akcji.

Kod przykładu jest dostępny na githubie.

1 thought on “Integracja Fluent Validation z ASP.NET MVC

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *