Sytuacja kobiet w IT w 2024 roku
17.04.20207 min
Artur Czopek

Artur Czopek Full-Stack Java Developersimplecoding.pl

Swagger – interaktywna dokumentacja API

Sprawdź, jak dzięki Swaggerowi zaoszczędzisz czas i stworzysz interaktywną dokumentację API.

Swagger – interaktywna dokumentacja API

Wielu z Was zapewne zaczynając tworzyć swoją aplikację webową, martwiło się: “Jak to udokumentować?”, “Gdzie przechowywać spis wszystkich endpointów?” lub “Jak łatwo wysyłać zapytania do konkretnego endpointa bez użycia aplikacji?”. Znane problemy mają to do siebie, że ludzie szukają zawsze łatwego i przenośnego rozwiązania. Wyżej wymienione problemy w świecie Javy można łatwo rozwiązać za pomocą Swaggera.

Dzisiaj dowiesz się, jak dodać go do projektu i jak zrobić podstawową konfigurację. W następnej części pociągniemy temat w bardziej zaawansowanym kierunku.

Czym jest ten Swagger ?

Jak już się pewnie możesz domyśleć ze wstępu, Swagger to biblioteka, które pozwala łatwo wizualizować i korzystać z dostępnego w aplikacji API, przy okazji tworząc jego dokumentację. Podstawowa konfiguracja Swaggera trwa zazwyczaj kilkanaście minut. Jeżeli nie jesteś pewien, w co się pakujesz, możesz zobaczyć przykład stworzony przez autorów biblioteki tutaj.

W tym poście będę bazować na SpringFox, czyli nakładce na Swaggera, która jeszcze bardziej ułatwia dołączenie Swaggera do projektu korzystającego ze Springa.

Projekt bazowy

Podstawowy projekt wygenerujemy sobie ze strony Springa, która ułatwia generowanie podstawowych projektów. Przejdźmy więc tutaj. Jeżeli chodzi o system buildów, standardowo będę bazować na Gradle’u. Z dependencji potrzebna nam będzie tylko część webowa (wpisz “web” w “Search for dependencies”). U mnie wygląda to tak:

Po wygenerowaniu projektu struktura powinna wyglądać nastepująco:

./gradlew bootRun


Przejdź na tę stronę. Jeżeli zobaczysz błąd taki, jak poniżej, to znaczy, że aplikacja…działa. Poważnie.

Pierwsze zapytania do aplikacji

Automatyczna dokumentacja i zgrabne wyświetlenie API to świetna rzecz. Trudno jednak dokumentować aplikację, jeżeli nie ma żadnych funkcjonalności. Jeżeli tu jesteś, to zakładam, że podstawy Springa oraz pojęcie o zapytaniach HTTP już masz. Inaczej nie jestem w stanie zagwarantować, że wszystko będzie dla Ciebie jasne (mimo tego, że naprawdę się staram).

Aby mieć na czym operować, stworzymy pierwszy REST-owy kontroler. Przejdź do głównego package’u (u mnie to będzie pl.arturczopek) i stwórz klasę HelloController, a w nim prostą metodę hello(), która będzie zwracała wiadomość “Hello, swagger!”. Powinno to wyglądać tak:

public class HelloController {
 
    public String hello() {
        return "Hello, swagger!";
    }
}


Następnie naniesiemy odpowiednie adnotację na nasz kontroller.

  • dodaj do klasy adnotację @RestController. Mówi ona, że nasza klasa jest kontrolerem REST-owym.
  • dodaj do metody hello() adnotację @RequestMapping(“/hello”). Mówi ona, że jeżeli w przeglądarce przejdziemy pod adres /hello, ujrzymy wynik wywołania funkcji hello() w postaci “prostej” lub jako JSON dla bardziej zaawansowanych struktur. Zapewnia nam to dodanie adnotacji @RestController do kontrolera. U nas zwracany jest String, więc ujrzymy prostą wiadomość. Domyślnie dla adnotacji @RequestMapping() zapytania są typu GET.

Po naniesionych adnotacjach kontroler powinien wyglądać tak:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class HelloController {
 
    @RequestMapping("/hello")
    public String hello() {
        return "Hello, swagger!";
    }
}


Mamy kontroler. Powinien on nam pokazać wiadomość. Uruchom aplikację ponownie i przejdź tutaj. Jeżeli wszystko poszło dobrze, to powinieneś zobaczyć w przeglądarce prostą wiadomość, zdefiniowaną wcześniej, czyli:

Hello, swagger!

Dodatkowe endpointy

Rozbudujmy aplikację o dodatkowe endpointy. Poziomem skomplikowania nie będą się one różnić za bardzo od tego wcześniejszego, ale wykorzystamy parę możliwych rodzajów zapytań HTTP, takie jak GET, POST i DELETE. Przykładowe endpointy, jakie stworzyłem:

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class HelloController {
 
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return "Hello, swagger!";
    }
 
    @RequestMapping(value = "/hello", method = RequestMethod.POST)
    public void helloOnConsole() {
        System.out.println("Hello, swagger on console!");
    }
 
    @RequestMapping(value = "/bye/{name}", method = RequestMethod.DELETE)
    public void byePerson(@PathVariable String name) {
        System.out.println("Bye bye, " + name);
    }
}


Jak widzisz, dodałem w definicjach endpointów rodzaj zapytań, jakie będą one konsumować. Kolejność taka, jak wypisana powyżej. Żeby było ciekawiej, endpoint przyjmujący zapytania GET i POST, ma taki sam adres. Zobaczysz jednak dalej (o ile jeszcze nie wiesz, jak to zrobić), że String to bez problemu rozróżni. Zapytania POST i DELETE nic nie zwracają. Nie chcemy otrzymywać nic, prócz domyślnego statusu powodzenia, czyli kodu HTTP 200.

Ciekawostką może być dla Ciebie ostatni endpoint dla rodzaju zapytania DELETE. Zacznijmy może od adresu:

/bye/{name}


Widzimy nazwę name “w wąsach”. Oznacza to, że możemy tutaj przesyłać jakąś wartość w adresie i odwołać się do niej jak do zmiennej. Definiujemy to w argumentach funkcji:

public void byePerson(@PathVariable String name)


Jak widzisz, dodajemy tutaj adnotację @PathVariable, która będzie w adresie szukać wartości pod zmienną określoną w adresie o takiej samej nazwie, jak nazwa zmiennej. W tym przypadku - name. Można to oczywiście parametryzować, ale dobrą praktyką jest używać tu i tu takich samych nazw. Jeżeli wiesz jak, to po ponownym uruchomieniu możesz wysłać na odpowiednie endpointy zapytania odpowiadającego im typu i sprawdzić w konsoli, czy faktycznie wszystko działa. W tym celu polecam użyć Postmana.

Na tę chwilę mamy tylko 3 endpointy, ale pewnie wiesz, że większe aplikacje mają ich nieraz 10 lub 100 razy więcej. W pewnym momencie ciężko nad tym zapanować. Jakie mamy adresy, co gdzie możemy wysłać…no, robi się ciężko. Teraz jest odpowiedni moment dla Swaggera, aby nam pomógł. Doczekałeś się.

Swagger, come on!

Aby skorzystać z jego mocy, Swagger musi w projekcie w ogóle się znaleźć. Dodamy go do dependencji w pliku build.gradle, a właściwie nakładkę, o której wcześniej wspomniałem. W pliku, tym w bloku dependencies, dodaj dwie linie:

compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.8.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.8.0'


Aby jednak pobrać te zależności, potrzebujemy dodać odpowiednie repozytorium. Wystarczy w bloku repositories dodać tylko wywołanie funkcji jcenter(). Całościowo plik build.gradle prezentuje się więc następująco:

import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
@EnableSwagger2
@Configuration
public class SwaggerConfig {
 
}


Swagger już dołączony, teraz należy go skonfigurować. Zaczniemy więc od stworzenia klasy SwaggerConfig. W niej będziemy trzymać całą konfigurację. Aby Swagger był dostępny, trzeba klasę oznaczyć adnotacją @EnableSwagger2, która mówi Springowi, by dodał Swaggera. Drugą w tym przypadku potrzebną adnotacją jest @Configuration, która mówi, że klasa posiada konfigurację. W tym przypadku – tak tak – jest to konfiguracja Swaggera. Zasadniczo mógłbyś dodać te adnotacje do głównej klasy aplikacji, ale z czasem, gdy Twoja konfiguracja będzie się rozrastać, warto mieć odpowiednie konfiguracje odseparowane od siebie.

plugins {
    id 'org.springframework.boot' version '2.1.3.RELEASE'
    id 'java'
}
 
apply plugin: 'io.spring.dependency-management'
 
group = 'pl.arturczopek'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
 
repositories {
    mavenCentral()
}
 
dependencies {
    compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.8.0'
    compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.8.0'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}


Po ponownym uruchomieniu aplikacji i przejściu pod ten adres, zobaczysz Swaggera, bazującego już na podstawowej konfiguracji.

Jak widzisz, już mamy wypisane dwa kontrolery: Basic Error Controller, który jest kontrolerem domyślnie tworzonym przez Springa oraz nasz Hello Controller. Tam faktycznie mamy 3 endpointy typu DELETE, GET, oraz POST. Każdy rodzaj zapytania ma swój kolor. Warto też zauważyć, że z prawej mamy wypisaną nazwę funkcji, która jest pod danym endpointem. Po kliknięciu w niego, rozwinie się cała lista tego, co możemy tam wysłać, co może być zwrócone itd. Jest ona interaktywna. Oznacza to, że możemy za jej pośrednictwem pod odpowiedni adres wysłać zapytanie. Kliknij na przykład w link "/bye/{name}":

Jak widzisz, mamy pokazany wymagany parametr name. Tutaj możemy wpisać jego wartość. Poniżej mamy wypisane kody statusów HTTP i odpowiadające im wiadomości, jakie endpoint może nam zwrócić. Spróbuj wpisać jako name swoje imię, a następnie zobacz, co jest wypisane w konsoli aplikacji. Powinieneś zobaczyć komunikat, który Cię żegna ?

Jeżeli chodzi o wartości podawane jako parametr, może być to czasem cały XML lub JSON, zależy to od endpointu i jego konfiguracji. Wtedy nie wysyłamy de facto parametru, a całe ciało zapytania. Swagger radzi sobie z tym perfekcyjnie. Zamiast wymaganej wartości, możesz zdefiniować endpoint tak, aby jako argument w ciele zapytania, przyjmował bardziej złożoną klasę. Zobaczysz w Swaggerze całą jego strukturę. Nie będę tu jednak tego omawiać. Dla ułatwienia dodam tylko, że wystarczy poszukać, czym jest w Springu adnotacja @RequestBody.

Podsumowanie

Zobaczyłeś, jak dodać Swaggera do projektu. Konfiguracja była banalna, ale wszystkie parametry na tę chwilę są domyślne. Pokażę Ci jednak w następnej części, jak można konfigurację dostosować pod swoje potrzeby, co pozwoli wykorzystać moc tego narzędzia w pełni. Myślę jednak, że już dostrzegasz benefity i moc, jaka drzemie w tej bibliotece. Ja sam również korzystam ze Swaggera.

Kod z pierwszej części dostępny na moim GitHubie.

<p>Loading...</p>