Cześć. Dzisiejszy wpis będzie drugą i ostatnią częścią o ile chodzi o wprowadzenie do Swaggera. W pierwszej części mogłeś się dowiedzieć co to jest ten Swagger i co można dzięki niemu zrobić. Pokazałem tam jak go dołączyć do projektu. Konfiguracja jednak była uboga, a na pewno chciałbyś mieć to bardziej “zcustomizowane”. W moim projekcie My Coach poświęciłem więcej czasu w zadbanie o detale. Pokażę Ci parę “tricków” konfiguracyjnych. Jest ich na pewno znacznie więcej, ale to mi w zupełności wystarczyło. Tobie na początku też powinno.
Własny opis
W pierwszej części mogłeś zobaczyć iż minimalna konfiguracja ma to do siebie, iż wiele rzeczy jest ustawionych domyślnie. Opis dokumentacji w Swaggerze wygląda tak:
Cóż, w wersji SpringFox, którą omawiam, można łatwo to zmienić. Wystarczy zdefiniować swojego beana, który będzie typu Docket. Przykładowo, w mojej klasie konfiguracyjnej Swaggera, można zrobić to dodając beana tak:
Jak widzisz, zwracamy tutaj obiekt typu…Docket. W konstruktorze musimy określić mu jakiego typu dokumentacji potrzebujemy. W tym przypadku jest to Swagger w wersji 2. Mamy możliwość ustawienia też dokumentacji dla starszych wersji, ale nie jest to dla nas interesujące. Drugą częścią jest zdefiniowanie apiInfo. Ja to robię definiując funkcję, która zwraca odpowiedni obiekt. W tym obiekcie definiujemy informacje, których potrzebujemy. Przykładowo, może to wyglądać tak:
Jak widzisz, użyłem tutaj buildera, gdyż nie potrzebuję ustawić wszystkich możliwych parametrów, a ten wzorzec projektowy jest tutaj zaimplementowany. W tym przypadku nadpisałem tytuł dokumentacji, opis, wersję oraz dane kontaktowe. Słowa pisane dużymi literami to zmienne typu string. Możesz tam wpisać cokolwiek. Warto zauważyć tylko, iż sekcja kontakt ma dedykowaną klasę. Nie jest to jednak problem, dane też można łatwo nadpisać. Pola, jakich nie nadpisałem to licencja, adres url licencji oraz adres do zasad usługi. Nic, co bym potrzebował. Dzięki opisanym tu trickom, dokumentacja w My Coach ma taki opis:
Teraz wiadomo, co to tak naprawdę jest. o ile używasz tego tylko dla siebie to nadpisywanie tych danych nie jest konieczne. Mimo wszystko, uważam, iż jest to przyjemniejszy widok niż “Apache’owe” informacje. Ty oczywiście możesz nadpisać więcej lub mniej pól. Możesz tego choćby w ogóle nie robić
Własny opis poprzez adekwatności aplikacji
Wiemy już, jak nadpisać defaultowy opis. Konfiguracja jednak tych parametrów w osobnej klasie nie wszystkim może się podobać. Dobrze mieć całą konfigurację w jednym pliku. o ile rozwijasz aplikacje w Springu, to na pewno korzystasz z plików takich jak application.yml lub application.properties. Tam można wiele rzeczy ustawić, nadpisać (szczególnie w Spring Boocie). Świetną opcją jest trzymanie tam też opisu dokumentacji. W tym celu, wydziel twoje opisy do zmiennych w obrębie klasy. U mnie wygląda to tak:
Następnie, pola te muszą mieć settery. Chyba nie muszę omawiać jak to zrobić? Ja w tym celu używam adnotacji @Setter z Lomboka.
Teraz użyjemy znaną i lubianą adnotację springową – @ConfigurationProperties(). Pozwala ona nam na “zmapowanie” adekwatności z pliku application.yml lub application.properties na pola w obrębie danej klasy. No dobrze, ale skąd on ma wiedzieć, które adekwatności może “zmappować”? Możemy to zrobić np poprzez szukaniu tych adekwatności, które mają wspólny prefix. Jest to bardzo proste. Musimy dodać do adnotacji tylko wartość prefix właśnie. U mnie wspólne parametry dla konfiguracji zaczynają się od “myCoach.swagger”. Klasa jest oznaczona w ten sposób:
Natomiast w pliku konfiguracyjnym application.yml pola są zdefiniowane w ten sposób:
Jak widać, pola faktycznie mają takie same nazwy jak pola w klasie konfiguracyjnej. Spostrzegawcze osoby mogły zauważyć tutaj kolejny parametr – path. Omówię go w dalszej części.
Dodatkowy link do API
Na pewno zauważyłeś, iż aby przejść do Swaggera to trzeba wpisywać adres “/swagger-ui.html”. Może to nie być zbyt fajne według Ciebie…można dodać endpoint, który będzie w stanie Cię przekierować do tej strony, a będzie bardziej samoopisujący się. Niestety, nie wiem jak go po prostu nadpisać (możliwe, iż się nie da), ale lepsze to niż nic.
W tym celu stworzyłem nowy kontroler, który nazywa się – uwaga, uwaga – SwaggerController. Warto zaznaczyć, iż to jest zwykły kontroler, nie RESTowy!
Zdefiniowanie kontrolera nie wystarczy. W tym celu trzeba stworzyć endpoint, który nas przekieruje. U mnie wygląda to tak:
No to teraz po kolej:
- SWAGGER_URL to jest link, który chcemy zwrócić, a adekwatnie całe polecenie. redirect: mówi nam, iż będzie to przekierowanie, natomiast /swagger-ui.html to strona, do której przekierowanie się ma odbyć
- adnotacja @RequestMapping z metodą GET jest nam znana. Może Cię zastanowić ta wartość z dolarem “w wąsach” – ${myCoach.swagger.path}. Można tutaj jako wartość endpointa wpisać zwykły adres. Ja natomiast używam zmiennej o podanej właśnie nazwie z konfiguracji application.yml. Odbywa się to praktycznie w ten sam sposób co w przypadku konfiguracji opisu poprzez ustawienie zmiennych opisanych punkt wcześniej. U mnie ta adekwatność ma wartość “rest-api”
- funkcja swaggerMapping() może dowolnie się nazywać. Ważne, aby zwracała stringa, który jest adresem przekierowania, czyli w tym przypadku zmienną SWAGGER_URL.
Dzięki takiej konfiguracji na swojej maszynie mogę dostać się do Swaggera wchodząc na adres http://localhost:8080/rest-api, gdzie nastąpi przekierowanie do odpowiedniej strony.
Brak “śmieciowych” endpointów
Domyślnie Swagger wyłapuje wszystkie możliwe endpointy. Dosłownie. Czasami niektóre nas nie obchodzą, jak na przykład w tym przypadku:
Jak już wspominałem, są to domyślne kontrolery springowe dla strony gdy mamy nieobsłużony błąd. Załóżmy, iż w Swaggerze nie chcemy ich widzieć. Z podstawową znajomością wyrażeń regularnych (regex) jesteśmy w stanie to zrobić. Samych wyrażeń regularnych dokładnie omawiać nie będę. o ile chciałbyś jednak z nimi trochę spędzić czasu to zachęcam Cię do sprawdzenia tej strony. Wracając to tematu, musimy odpowiednie wyrażenie regularne stworzyć i dodać do konfiguracji, a adekwatnie do beana typu Docket.
Doszło tutaj wywołanie funkcji paths() dla klasy Docket. Jako parametr musimy przekazać obiekt, który implementuje interfejs Predicate z biblioteki Google Guava. Zaimplementujemy odpowiedni regex w funkcji o takiej samej nazwie, którą tutaj przekazujemy jako atrybut. Funkcja może wyglądać na przykład tak:
Co tu się stało tak naprawdę:
- zdefiniowaliśmy zmienną o nazwie NO_ERROR_REGEX, której wartość jest wyrażeniem regularnym mówiącym, iż w adresie endpointu nie może znaleźć się słowo “error”.
- w funkcji paths() zwracamy wartość funkcji and(), która przyjmuje jako argumenty wyrażenia regularne, które muszą być spełnione. Jak się prawdopodobnie domyślasz po nazwie funkcji, WSZYSTKIE wyrażenia regularne muszą być spełnione. U mnie jest tylko jeden regex, ale nic nie stoi na przeszkodzie by dodać ich więcej. Oczywiście, and() nie jest jedyną możliwą funkcją. Guava oferuje nam też między innymi funkcje not() lub or(), których nazw chyba tłumaczyć nie muszę.
Po zmianie konfiguracji i odpaleniu Swaggera jeszcze raz, wszystkie endpointy zawierające w adresie słowo “error” powinny być niewidoczne:
Swagger dostępny tylko w trybie developerskim
Zebranie wszystkich endpointów w jedno miejsce jest świetne. Może się jednak zdarzyć, iż chcemy korzystać z tego tylko podczas rozwijania aplikacji. “Na produkcji” niekoniecznie. W tym celu Spring umożliwia nam definiowanie różnych profili. Możemy dzięki nim dostosowywać konfigurację w zależności od potrzeb. Osobiście dużo korzystam z tej możliwości. Jako, iż chcę Swaggera używać tylko w profilu developerskim to muszę klasę z konfiguracją oznaczyć adnotacją, w której należy wpisać w przypadku jakiego profilu/profili klasa powinna być aktywowana. Adnotacja która tutaj nam pomoże – znowu zaskoczenie – to @Profile(). W moim przypadku profil developerski nazywa się “dev”, więc kontroler oraz klasę konfiguracyjną oznaczyłem w ten sposób:
Po uruchomieniu aplikacji z innym aktywnym profilem (np “prod”, ustawiamy w tym celu w Springu adekwatność spring.profiles.active: prod) i przejściu do adresu ze Swaggerem powinniśmy ujrzeć stronę z błędem:
Podsumowanie
Myślę, iż utwierdziłem Cię w przekonaniu, iż Swagger jest potężną biblioteką, a w połączeniu ze Springiem dostosowywanie konfiguracji jest banalne. Po raz kolejny gorąco zachęcam Cię do korzystania z tego, co oferuje nam Swagger. Mam nadzieję, iż te dwa wpisy dały Ci konkretną wartość. Zapraszam do przemyśleń, jestem otwarty na sugestie. Do przeczytania!