Spring REST Docs vs OpenAPI

REST Top

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN

1. Oversigt

Spring REST Docs og OpenAPI 3.0 er to måder at oprette API-dokumentation på til en REST API.

I denne vejledning undersøger vi deres relative fordele og ulemper.

2. En kort oversigt over oprindelser

Spring REST Docs er en ramme udviklet af Spring Community for at skabe nøjagtig dokumentation for RESTful API'er. Det tager en testdrevet tilgang, hvor dokumentationen er skrevet enten som Spring MVC-tests, Spring Webflux's WebTestClient, eller REST-forsikret.

Resultatet af at køre testene oprettes som AsciiDoc-filer, som kan sammensættes ved hjælp af Asciidoctor til at generere en HTML-side, der beskriver vores API'er. Da den følger TDD-metoden, bringer Spring REST Docs automatisk alle sine fordele ind såsom mindre fejlberettiget kode, reduceret omarbejdning og hurtigere feedback-cyklusser for at nævne nogle få.

OpenAPI er derimod en specifikation, der er født ud af Swagger 2.0. Dens seneste version i skrivende stund er 3.0 og har mange kendte implementeringer.

Som enhver anden specifikation ville, opstiller OpenAPI visse grundregler for dets implementeringer at følge. Kort sagt, alt OpenAPI-implementeringer skal producere dokumentationen som et JSON-objekt, enten i JSON- eller YAML-format.

Der findes også mange værktøjer, der tager denne JSON / YAML ind og spytter et brugergrænseflade til at visualisere og navigere i API'en. Dette kommer f.eks. Godt med accepttest. I vores kodeeksempler her bruger vi springdoc - et bibliotek til OpenAPI 3 med Spring Boot.

Inden vi ser nærmere på de to, lad os hurtigt oprette en API, der skal dokumenteres.

3. REST API

Lad os sammensætte en grundlæggende CRUD API ved hjælp af Spring Boot.

3.1. Datalageret

Her er det lager, som vi bruger, bare bare ben PagingAndSortingRepository interface med modellen Foo:

@Repository offentlig grænseflade FooRepository udvider PagingAndSortingRepository {} @Entity public class Foo {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat lang id; @Column (nullable = false) privat streng titel; @Column () private strengorganer; // konstruktør, getters og setters}

Vi indlæser også arkivet ved hjælp af en skema.sql og en data.sql.

3.2. Controlleren

Lad os derefter se på controlleren, springe dens implementeringsoplysninger for kortfattethed:

@RestController @RequestMapping ("/ foo") offentlig klasse FooController {@Autowired FooRepository repository; @GetMapping offentlig ResponseEntity getAllFoos () {// implementering} @GetMapping (værdi = "{id}") offentlig ResponseEntity getFooById (@PathVariable ("id") Lang id) {// implementering} @PostMapping offentlig ResponseEntity addFoo (@RequestBody @Valid Foo foo ) {// implementering} @DeleteMapping ("/ {id}") offentlig ResponseEntity deleteFoo (@PathVariable ("id") lang id) {// implementering} @PutMapping ("/ {id}") offentlig ResponseEntity updateFoo (@ PathVariable ("id") lang id, @RequestBody Foo foo) {// implementering}}

3.3. Ansøgningen

Og endelig Boot App:

@SpringBootApplication () public class Application {public static void main (String [] args) {SpringApplication.run (Application.class, args); }}

4. OpenAPI / Springdoc

Lad os nu se hvordan springdoc kan tilføje dokumentation til vores Foo REST API.

Husk det det genererer et JSON-objekt og en UI-visualisering af API'en baseret på det pågældende objekt.

4.1. Grundlæggende brugergrænseflade

Til at begynde med tilføjer vi bare et par Maven-afhængigheder - springdoc-openapi-data-rest til generering af JSON og springdoc-openapi-ui til gengivelse af brugergrænsefladen.

Værktøjet introspekterer koden til vores API og læser kontrolmetodernes kommentarer. På det grundlag genererer det API JSON, som vil være live på // localhost: 8080 / api-docs /. Det tjener også en grundlæggende brugergrænseflade kl //localhost:8080/swagger-ui-custom.html:

Som vi kan se, uden at tilføje nogen kode overhovedet, opnåede vi en smuk visualisering af vores API helt ned til Foo skema. Bruger Prøve det knappen, kan vi endda udføre operationerne og se resultaterne.

Nu, hvad hvis vi ville tilføje noget ægte dokumentation til API'et? Med hensyn til hvad API'et handler om, hvad alle dets operationer betyder, hvad skal der indtastes, og hvilke svar kan man forvente?

Vi ser på dette i det næste afsnit.

4.2. Detaljeret brugergrænseflade

Lad os først se, hvordan du tilføjer en generel beskrivelse til API'et.

Til det tilføjer vi en OpenAPI bønne til vores Boot App:

@Bean offentlig OpenAPI customOpenAPI (@Value ("$ {springdoc.version}") String appVersion) {returner ny OpenAPI (). Info (ny Info () .title ("Foobar API") .version (appVersion). Beskrivelse ( "Dette er en eksempel Foobar-server oprettet ved hjælp af springdocs -" + "et bibliotek til OpenAPI 3 med spring boot.") .TermsOfService ("// swagger.io/terms/") .license (ny licens (). Navn (" Apache 2.0 ") .url (" // springdoc.org "))); } 

For at tilføje nogle oplysninger til vores API-operationer dekorerer vi derefter vores kortlægninger med et par OpenAPI-specifikke kommentarer.

Lad os se, hvordan vi kan beskrive getFooById. Vi gør det inde i en anden controller, FooBarController, der ligner vores FooController:

@RestController @RequestMapping ("/ foobar") @Tag (navn = "foobar", beskrivelse = "foobar API med dokumentationsbemærkninger") offentlig klasse FooBarController {@Autowired FooRepository repository; @Operation (summary = "Få en foo ved foo id") @ApiResponses (værdi = {@ApiResponse (responsCode = "200", beskrivelse = "fundet foo", content = {@Content (mediaType = "application / json") , skema = @Schema (implementering = Foo.class))}), @ApiResponse (responsCode = "400", beskrivelse = "Ugyldig id leveret", indhold = @Content), @ApiResponse (responsCode = "404", beskrivelse = "Foo ikke fundet", indhold = @Content)}) @GetMapping (værdi = "{id}") offentlig ResponseEntity getFooById (@Parameter (beskrivelse = "id for foo der skal søges") @PathVariable ("id") Streng id) {// implementering udeladt for kortfattethed} // andre tilknytninger, lignende kommenteret med @Operation og @ApiResponses} 

Lad os nu se effekten på brugergrænsefladen:

Så med disse minimale konfigurationer kan brugeren af ​​vores API nu se, hvad det handler om, hvordan man bruger det, og hvilke resultater man kan forvente. Alt, hvad vi skulle gøre var at kompilere koden og køre Boot App.

5. Spring REST Docs

REST-dokumenter er en helt anden tilgang til API-dokumentation. Som beskrevet tidligere er processen testdrevet, og output er i form af en statisk HTML-side.

I vores eksempel her, vi bruger Spring MVC Tests til at oprette dokumentationsuddrag.

Fra starten skal vi tilføje spring-restdocs-mockmvc afhængighed og asciidoc Maven-plugin til vores pom.

5.1. JUnit5-testen

Lad os nu se på JUnit5-testen, der inkluderer vores dokumentation:

@ExtendWith ({RestDocumentationExtension.class, SpringExtension.class}) @SpringBootTest (klasser = Application.class) offentlig klasse SpringRestDocsIntegrationTest {private MockMvc mockMvc; @Autowired privat ObjectMapper objectMapper; @BeforeEach offentlig tomrumsopsætning (WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {this.mockMvc = MockMvcBuilders.webAppContextSetup (webApplicationContext) .apply (documentConfiguration (restDocumentation)). } @Test offentlig ugyldig nårGetFooById_thenSuccessful () kaster Undtagelse {ConstraintDescriptions desc = new ConstraintDescriptions (Foo.class); this.mockMvc.perform (get ("/ foo / {id}", 1)). og Expect (status (). isOk ()). andDo (document ("getAFoo", preprocessRequest (prettyPrint ()), preprocessResponse (prettyPrint ()), pathParameters (parameterWithName ("id"). beskrivelse ("id for foo, der skal søges")), responseFields (fieldWithPath ("id"). beskrivelse ("Id for foo" + collectionToDelimitedString (desc.descriptionsForProperty) ("id"), ".")), fieldWithPath ("title"). beskrivelse ("Foo's titel"), fieldWithPath ("body"). beskrivelse ("The foo body")))) ; } // flere testmetoder til at dække andre tilknytninger

}

Efter at have kørt denne test får vi flere filer i vores mål / genereret-uddrag bibliotek med oplysninger om den givne API-handling. Især, whenGetFooById_thenSuccessful vil give os otte adocs i en getAFoo mappe i telefonbogen.

Her er en prøve http-respons.adoc, der naturligvis indeholder responsorganet:

[kilde, http, optioner = "nowrap"] ---- HTTP / 1.1 200 OK Indholdstype: applikation / json Indholdslængde: 60 {"id": 1, "title": "Foo 1", "body ":" Foo body 1 "} ----

5.2. fooapi.adoc

Nu har vi brug for en masterfil, der fletter alle disse uddrag sammen for at danne en velstruktureret HTML.

Lad os kalde det fooapi.adoc og se en lille del af det:

=== Adgang til foo GET En `GET` anmodning bruges til at få adgang til foo read. ==== Anmodningsstruktur inkluderer :: {snippets} /getAFoo/http-request.adoc [] ==== Stiparametre inkluderer :: {snippets} /getAFoo/path-parameters.adoc [] ==== Eksempel på svar inkluderer :: {snippets} /getAFoo/http-response.adoc [] ==== CURL-anmodning inkluderer :: {snippets} /getAFoo/curl-request.adoc []

Efter udførelse af asciidoctor-maven-plugin, får vi den endelige HTML-fil fooapi.html i mål / genereret-dokumenter folder.

Og sådan ser det ud, når det åbnes i en browser:

6. Nøgleudtag

Nu hvor vi har set på begge implementeringer, lad os sammenfatte fordele og ulemper.

Med springdoc, de bemærkninger, vi var nødt til at bruge, rodede vores hvilestyrings kode og reducerede læsbarheden. Dokumentationen var også tæt koblet til koden og ville komme i produktion.

Det er overflødigt at sige, at vedligeholdelse af dokumentationen er en anden udfordring her - hvis noget i API'et ændres, ville programmøren altid huske at opdatere den tilsvarende OpenAPI-kommentar?

På den anden side, REST Docs ser hverken så fængende ud som den anden brugergrænseflade, og den kan heller ikke bruges til acceptstest. Men det har sine fordele.

Især den vellykkede afslutning af Spring MVC-testen giver os ikke kun uddragene, men verificerer også vores API, som enhver anden enhedstest ville. Dette tvinger os til at foretage dokumentationsændringer svarende til eventuelle API-ændringer. Dokumentationskoden er også helt adskilt fra implementeringen.

Men igen på bagsiden, vi måtte skrive mere kode for at generere dokumentationen. For det første selve testen, der uden tvivl er lige så detaljeret som OpenAPI-kommentarerne, og for det andet mesteren adoc.

Det har også brug for flere trin for at generere den endelige HTML - køre testen først og derefter pluginet. Springdoc krævede kun, at vi kørte Boot-appen.

7. Konklusion

I denne vejledning kiggede vi på forskellene mellem OpenAPI-baserede springdoc og Spring REST Docs. Vi så også, hvordan man implementerer de to for at generere dokumentation til en grundlæggende CRUD API.

Sammenfattende har begge deres fordele og ulemper, og beslutningen om at bruge den ene over den anden er underlagt vores specifikke krav.

Som altid er kildekoden tilgængelig på GitHub.

REST bunden

Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:

>> KONTROLLER KURSEN

$config[zx-auto] not found$config[zx-overlay] not found