Spring Boot integracija Swagger-UI za online API dokumentaciju
O Swagger-u
Swagger je web servis za generisanje, opisivanje i pozivanje RESTful interfejsa.

Ukoliko želite da razumete RESTful arhitekturu, posetite blog Ruan Yifeng-a: https://www.ruanyifeng.com/blog/2011/09/restful.html
Drugim rečima, Swagger prikazuje interfejse koje projekat želi da izloži direktno na stranici, pa developeri mogu odmah pozvati interfejs i testirati ga, što u velikoj meri povećava efikasnost razvoja.
Na primer, ako je backend programer napisao login interfejs i želi da testira da li njegov interfejs radi kako se očekuje, mora prvo da simulira ponašanje korisnika pri prijavi — uključujući normalno ponašanje (unos ispravnog korisničkog imena i lozinke) i izuzetke (unos pogrešnog korisničkog imena i lozinke) — što je noćna mora.
Ali uz Swagger, uz jednostavnu konfiguraciju može se generisati stranica sa prikazom interfejsa, request parametri i rezultati se prikazuju vizuelno, a pruža se i praktičan servis za testiranje.
- Frontend programeri mogu putem stranice sa prikazom interfejsa videti koje request parametre treba da pošalju i koji format povratnih podataka treba da budu, pa backend programer ne mora više ručno da piše dokumentaciju interfejsa;
- Backend programeri mogu putem stranice sa prikazom interfejsa testirati i proveriti da li njihov interfejs radi kako se očekuje, čime se smanjuje cena debagovanja u fazi razvoja.
Tada razdvajanje frontend i backend-a može vrlo lepo da se sprovede, zar ne?
Zvanični sajt Swagger: https://swagger.io/
Pre Swagger-a, situacija je bila prilično loša. Frontend je često žalio da im backend dostavljena dokumentacija interfejsa ne odgovara stvarnosti. S druge strane, backend je smatrao da pisanje i održavanje dokumentacije interfejsa zahteva puno truda, pa su je često stizali da ažuriraju.
Svi su bili nemilosrdno mučeni i patili bez kraja...
Swagger definiše skup specifikacija: dovoljno je da prema njegovim pravilima definišete interfejse i informacije vezane za njih, a zatim pomoću niza alata izvedenih iz Swagger-a možete generisati dokumentaciju interfejsa u različitim formatima, čak i klijent i server kod na više jezika, kao i stranicu za online debagovanje interfejsa.
Tako, uz pravovremeno ažuriranje Swagger opisne datoteke, dokumentacija interfejsa se može automatski generisati, čime se postiže doslednost između koda na strani pozivaoca, koda na strani servera i dokumentacije interfejsa.
Integracija Swagger-UI
Swagger-UI je set HTML/CSS/JS okvira za renderovanje Swagger dokumentacije, kako bi se pružio lepši interfejs za API dokumentaciju.
Drugim rečima, Swagger-UI je vizuelna komponenta za renderovanje koju pruža Swagger, a podržava online uvoz opisnih datoteka i lokalno pokretanje UI projekta.

Prvi korak, u pom.xml datoteku dodajte Swagger starter.
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>Hm, zar nismo rekli da dodajemo Swagger zavisnost? Zašto onda dodajemo springfox-boot-starter?
To je zato što:
- Swagger jeste specifikacija.
- springfox-swagger je implementacija Swagger specifikacije zasnovana na Spring ekosistemu.
- springfox-boot-starter je starter koji springfox pruža za Spring Boot projekte; on pojednostavljuje uvoz Swagger zavisnosti, jer bismo inače u pom.xml morali da dodamo više zavisnosti poput springfox-swagger, springfox-swagger-ui itd.
Drugi korak, dodajte Java konfiguraciju za Swagger.
@Configuration
@EnableOpenApi
public class SwaggerConfig {
@Bean
public Docket docket() {
Docket docket = new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo()).enable(true)
.select()
// apis: dodaje opseg iz kojeg Swagger izdvaja interfejse
.apis(RequestHandlerSelectors.basePackage("top.codingmore.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Beleške o praktičnom projektu Codingmore")
.description("Codingmore je projekat sa razdvojenim frontendom i backendom u Spring Boot + Vue-u")
.contact(new Contact("Chenmo Wang Er", "https://codingmore.top","www.qing_gee@163.com"))
.version("v1.0")
.build();
}
}@Configuration anotacija se obično koristi za deklarisanje Java konfiguracione klase, koja zamenjuje nekadašnje XML konfiguracione datoteke i čini konfiguraciju jednostavnijom i direktnijom.
@EnableOpenApi anotacija ukazuje na to da je Swagger omogućen.
Klasa SwaggerConfig sadrži metodu
docket()deklarisanu sa @Bean anotacijom; tu metodu će skenirati Spring-ova klasa AnnotationConfigApplicationContext ili AnnotationConfigWebApplicationContext, a zatim je dodati u Spring kontejner.
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);Ukratko o sadržaju Swagger konfiguracije:
new Docket(DocumentationType.OAS_30): koristi se Swagger API verzije 3.0. OAS je skraćenica od OpenAPI Specification; Swagger prati upravo taj skup specifikacija.apiInfo(apiInfo()): konfiguriše osnovne informacije API dokumentacije — naslov, opis, autor, verziju itd.apis(RequestHandlerSelectors.basePackage("top.codingmore.controller")): navodi da je opseg API interfejsa controller kontroler.paths(PathSelectors.any()): navodi poklapanje sa svim URL-ovima.
Treći korak, dodajte klasu kontrolera.
@Api(tags = "Test Swagger")
@RestController
@RequestMapping("/swagger")
public class SwaggerController {
@ApiOperation("test")
@RequestMapping("/test")
public String test() {
return "Chenmo Wang Er je i zgodan i ružan";
}
}- @Api anotacija, primenjena na klasu, označava taj kontroler kao Swagger resurs. Ova anotacija ima 3 svojstva:
- tags: API-ji sa istim tagom biće grupisani i prikazani zajedno
- value: ako tags nije definisan, value se koristi kao tags API-ja.
- description: zastarelo
- @ApiOperation anotacija, primenjena na metodu, opisuje čemu ta metoda služi. Ova anotacija ima 4 svojstva:
- value: kratak opis operacije, dužine do 120 slova, odnosno 60 kineskih znakova.
- notes: detaljno objašnjenje operacije.
- httpMethod: naziv HTTP metode zahteva; moguće vrednosti su: "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" i "PATCH".
- code: podrazumevano 200; važeće vrednosti moraju odgovarati standardnim definicijama HTTP Status Code-a.
@RestController anotacija, primenjena na klasu, kombinovana je anotacija @ResponseBody + @Controller; ako metoda treba da vrati JSON, @ResponseBody anotacija se može izostaviti.
@RequestMapping anotacija se može primeniti na klasu (roditeljska putanja) i na metodu (podređena putanja), uglavnom služi za definisanje putanje i tipa API zahteva. Ova anotacija ima 6 svojstava:
- value: navodi stvarnu adresu zahteva
- method: navodi tip method zahteva, GET, POST, PUT, DELETE itd.
- consumes: navodi tip sadržaja zahteva koji se prihvata (Content-Type), na primer application/json, text/html
- produces: navodi tip sadržaja koji se vraća; vraća se samo ako tip (Accept) iz zaglavlja request zahteva sadrži navedeni tip
- params: navodi da zahtev mora sadržati određene vrednosti parametara
- headers: navodi da zahtev mora sadržati određene vrednosti zaglavlja
Četvrti korak, pokrenite servis i u pregledaču unesite http://localhost:8080/swagger-ui/ da biste pristupili API dokumentaciji koju je generisao Swagger.

Otvorite panel GET zahteva, kliknite na „try it out", a zatim na „execute" da vidite podatke koje interfejs vraća.

Nekompatibilnost verzija
Tokom integracije Swagger-a u Spring Boot, otkrio sam jedan veliki bug — Spring Boot verzija 2.6.7 i springfox verzija 3.0.0 nisu kompatibilni; pri pokretanju se odmah javlja greška.

Caused by: java.lang.NullPointerException: Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null
Praćenjem problema otkrio sam da je na GitHub-u potvrđeno da je neko prijavio taj bug u Spring Boot repozitorijumu.
Spring Boot kaže da je to bug u springfox-u.

Kad sam pregledao, zaista jeste.

Rešenje koje su neki predložili je prelazak na SpringDoc.

To zahteva zamenu anotacija @Api → @Tag, @ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar"), što nije baš prijateljski za stare projekte; za nove projekte, pak, možete odmah probati SpringDoc.
Još jedno predloženo rešenje je:
- prvo promeniti strategiju poklapanja u ant-path-matcher (application.yml).
spring:
mvc:
path match:
matching-strategy: ANT_PATH_MATCHER- zatim u Spring kontejner ubrizgati sledeći bean, koji se može staviti u klasu SwaggerConfig.
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}Adresa rešenja: https://github.com/springfox/springfox/issues/3462
Nakon ponovnog kompajliranja projekta, videćete da je greška nestala — mogu samo reći da su u GitHub issue sekciji svi majstori!
Pogledajte Swagger interfejs dokumentaciju i uverićete se da sve radi normalno.

Moram još jednom da naglasim: u GitHub issue sekciji su svi majstori! Kad god naiđete na problem, obavezno pogledajte issue sekciju.
Što se tiče toga zašto je to potrebno, autor rešenja je dao svoj odgovor.

Ukratko, springfox i Spring su se razišli po pitanju pathPatternsCondition, a ova dva koraka služe da uklone taj nesklad.
Pored toga, postoji još jedan konzervativniji pristup — direktno vratiti verziju Spring Boot-a na nižu, na primer 2.4.5.

Kratak rezime
Iako Swagger rešava problem nedoslednosti između koda na strani pozivaoca, koda na strani servera i dokumentacije interfejsa, iskreno — Swagger-UI je zaista previše ružan.
Putanja do izvornog koda
- Codingmore: https://github.com/itwanger/coding-more
- codingmore-swagger: https://github.com/itwanger/codingmore-learning
