Våren REST Docs vs OpenAPI

REST Topp

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

1. Oversikt

Spring REST Docs og OpenAPI 3.0 er to måter å lage API-dokumentasjon på for en REST API.

I denne opplæringen vil vi undersøke deres relative fordeler og ulemper.

2. En kort oppsummering av opprinnelsen

Spring REST Docs er et rammeverk utviklet av Spring-samfunnet for å lage nøyaktig dokumentasjon for RESTful APIer. Det tar en testdrevet tilnærming, hvor dokumentasjonen er skrevet enten som Spring MVC-tester, Spring Webflux's WebTestClient, eller REST-forsikret.

Resultatet av å kjøre testene opprettes som AsciiDoc-filer som kan settes sammen ved hjelp av Asciidoctor for å generere en HTML-side som beskriver API-ene våre. Siden den følger TDD-metoden, får Spring REST Docs automatisk inn alle fordelene som mindre feilutsatt kode, redusert etterarbeid og raskere tilbakemeldingssykluser, for å nevne noen.

OpenAPI, derimot, er en spesifikasjon født ut av Swagger 2.0. Den siste versjonen fra skriving av dette er 3.0 og har mange kjente implementeringer.

Som alle andre spesifikasjoner vil OpenAPI legge ut noen grunnregler for implementeringene de skal følge. Enkelt sagt, alt OpenAPI-implementeringer skal produsere dokumentasjonen som et JSON-objekt, enten i JSON- eller YAML-format.

Det finnes også mange verktøy som tar denne JSON / YAML inn og spytter ut et brukergrensesnitt for å visualisere og navigere i API. Dette kommer for eksempel godt under akseptanstesting. I kodeeksemplene våre her bruker vi springdoc - et bibliotek for OpenAPI 3 med Spring Boot.

Før vi ser nærmere på de to, la oss raskt sette opp en API som skal dokumenteres.

3. REST API

La oss sette sammen en grunnleggende CRUD API ved hjelp av Spring Boot.

3.1. Datalageret

Her er depotet vi skal bruke bare bein PagingAndSortingRepository grensesnitt, med modellen Foo:

@Repository offentlig grensesnitt FooRepository utvider PagingAndSortingRepository {} @Entity public class Foo {@Id @GeneratedValue (strategi = GenerationType.IDENTITY) privat lang id; @Column (nullable = false) privat strengtittel; @Column () private String body; // constructor, getters and setters}

Vi laster også depotet med a schema.sql og en data.sql.

3.2. Kontrolleren

Deretter, la oss se på kontrolleren, hoppe over dens implementeringsdetaljer for kortfattethet:

@RestController @RequestMapping ("/ foo") offentlig klasse FooController {@Autowired FooRepository repository; @GetMapping offentlig ResponseEntity getAllFoos () {// implementering} @GetMapping (verdi = "{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. Søknaden

Og til slutt, Boot App:

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

4. OpenAPI / Springdoc

La oss nå se hvordan springdoc kan legge til dokumentasjon til vår Foo REST API.

Husk det det vil generere et JSON-objekt og en UI-visualisering av API-en basert på det objektet.

4.1. Grunnleggende brukergrensesnitt

Til å begynne med legger vi bare til et par Maven-avhengigheter - springdoc-openapi-data-rest for å generere JSON, og springdoc-openapi-ui for gjengivelse av brukergrensesnittet.

Verktøyet vil introdusere koden for API-en vår, og lese kommentarene til kontrollmetodene. På den bakgrunn vil den generere API JSON som vil være live på // localhost: 8080 / api-docs /. Det vil også tjene et grunnleggende brukergrensesnitt på //localhost:8080/swagger-ui-custom.html:

Som vi kan se, uten å legge til noen kode i det hele tatt, fikk vi en vakker visualisering av API-en vår helt ned til Foo skjema. Bruker Prøv det knappen, kan vi til og med utføre operasjonene og se resultatene.

Nå, hva om vi ønsket å legge til ekte dokumentasjon i API-et? Når det gjelder hva APIen handler om, hva alle operasjonene betyr, hva skal vi legge inn, og hvilke svar kan du forvente?

Vi ser på dette i neste avsnitt.

4.2. Detaljert brukergrensesnitt

La oss først se hvordan du legger til en generell beskrivelse til API.

For det legger vi til en OpenAPI bean til vår Boot App:

@Bean offentlig OpenAPI customOpenAPI (@Value ("$ {springdoc.version}") String appVersion) {returner ny OpenAPI (). Info (ny Info () .title ("Foobar API") .versjon (appVersion). Beskrivelse ( "Dette er et eksempel på en Foobar-server opprettet ved hjelp av springdocs -" + "et bibliotek for OpenAPI 3 med vårstart.") .TermsOfService ("// swagger.io/terms/") .license (ny lisens (). Navn (" Apache 2.0 ") .url (" // springdoc.org "))); } 

Deretter, for å legge til litt informasjon i API-operasjonene våre, vil vi dekorere kartleggingen med noen OpenAPI-spesifikke merknader.

La oss se hvordan vi kan beskrive getFooById. Vi gjør dette inne i en annen kontroller, FooBarController, som ligner på vår FooController:

@RestController @RequestMapping ("/ foobar") @ Tag (navn = "foobar", beskrivelse = "foobar API med dokumentasjonsmerknader") offentlig klasse FooBarController {@Autowired FooRepository repository; @Operation (summary = "Få en foo av foo id") @ApiResponses (verdi = {@ApiResponse (responsCode = "200", beskrivelse = "fant foo", content = {@Content (mediaType = "application / json") , skjema = @Schema (implementering = Foo.class))}), @ApiResponse (responsCode = "400", beskrivelse = "Ugyldig ID leveres", innhold = @Content), @ApiResponse (responsCode = "404", beskrivelse = "Foo ikke funnet", content = @Content)}) @GetMapping (value = "{id}") offentlig ResponseEntity getFooById (@Parameter (beskrivelse = "id for foo som skal søkes") @PathVariable ("id") Streng id) {// implementering utelatt for kortfattethet} // andre kartlegginger, tilsvarende kommentert med @Operation og @ApiResponses} 

La oss nå se effekten på brukergrensesnittet:

Så med disse minimale konfigurasjonene kan brukeren av API-en vår nå se hva den handler om, hvordan man bruker den og hvilke resultater man kan forvente. Alt vi måtte gjøre var å kompilere koden og kjøre Boot App.

5. Våren REST Docs

REST-dokumenter er et helt annet syn på API-dokumentasjon. Som beskrevet tidligere er prosessen testdrevet, og utdataene er i form av en statisk HTML-side.

I vårt eksempel her, vi bruker Spring MVC-tester for å lage dokumentasjonsutdrag.

Først må vi legge til vår-restdocs-mockmvc avhengighet og asciidoc Maven plugin til vår pom.

5.1. JUnit5-testen

La oss ta en titt på JUnit5-testen som inkluderer dokumentasjonen vår:

@ExtendWith ({RestDocumentationExtension.class, SpringExtension.class}) @SpringBootTest (klasser = Application.class) offentlig klasse SpringRestDocsIntegrationTest {private MockMvc mockMvc; @Autowired privat ObjectMapper objectMapper; @BeforeEach public void setup (WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {this.mockMvc = MockMvcBuilders.webAppContextSetup (webApplicationContext) .apply (documentationConfiguration (restDocumentation)). } @Test offentlig ugyldig nårGetFooById_thenSuccessful () kaster Unntak {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 som skal søkes")), responseFields (fieldWithPath ("id") .description ("IDen til foo" + collectionToDelimitedString (desc.descriptionsForProperty) ("id"), ".")), fieldWithPath ("title"). beskrivelse ("The foo title"), fieldWithPath ("body"). beskrivelse ("The foo body")))) ; } // flere testmetoder for å dekke andre kartlegginger

}

Etter å ha kjørt denne testen får vi flere filer i vår mål / genererte utdrag katalog med informasjon om den gitte API-operasjonen. Særlig, whenGetFooById_thenSuccessful vil gi oss åtte adocs i en getAFoo mappen i katalogen.

Her er et utvalg http-respons.adocinneholder selvfølgelig responsorganet:

[kilde, http, alternativer = "nowrap"] ---- HTTP / 1.1 200 OK Innholdstype: applikasjon / json Innholdslengde: 60 {"id": 1, "title": "Foo 1", "body ":" Foo body 1 "} ----

5.2. fooapi.adoc

Nå trenger vi en hovedfil som vil flette alle disse kodebitene sammen for å danne en godt strukturert HTML.

La oss kalle det fooapi.adoc og se en liten del av den:

=== Tilgang til foo GET En `GET`-forespørsel brukes til å få tilgang til foo read. ==== Forespørselsstruktur inkluderer :: {snippets} /getAFoo/http-request.adoc [] ==== Baneparametere inkluderer :: {snippets} /getAFoo/path-parameters.adoc [] ==== Eksempel på svar inkluderer :: {snippets} /getAFoo/http-response.adoc [] ==== CURL-forespørsel inkluderer :: {snippets} /getAFoo/curl-request.adoc []

Etter å ha utført asciidoctor-maven-plugin, får vi den endelige HTML-filen fooapi.html i mål / generert-dokumenter mappe.

Og slik vil det se ut når det åpnes i en nettleser:

6. Viktige takeaways

Nå som vi har sett på begge implementeringene, la oss oppsummere fordeler og ulemper.

Med springdoc, kommentarene vi måtte bruke, rotet koden til hvilestyringen og reduserte lesbarheten. Også dokumentasjonen var tett koblet til koden og ville komme seg i produksjon.

Det er unødvendig å si at vedlikehold av dokumentasjonen er en annen utfordring her - hvis noe i API-et endret seg, ville programmereren alltid huske å oppdatere den tilsvarende OpenAPI-merknaden?

På den andre siden, REST Docs ser verken så fengende ut som det andre brukergrensesnittet gjorde, og det kan heller ikke brukes til godkjenningstesting. Men det har sine fordeler.

Spesielt vellykket gjennomføring av Spring MVC-testen gir oss ikke bare utdragene, men verifiserer også API-en vår som enhver annen enhetstest ville gjort. Dette tvinger oss til å gjøre dokumentasjonsendringer som tilsvarer API-endringer, hvis noen. Dokumentasjonskoden er også helt atskilt fra implementeringen.

Men igjen, på baksiden, vi måtte skrive mer kode for å generere dokumentasjonen. For det første selve testen som uten tvil er like ordentlig som OpenAPI-kommentarene, og for det andre mesteren adoc.

Det trenger også flere trinn for å generere den endelige HTML - å kjøre testen først og deretter plugin. Springdoc bare krevde at vi kjørte Boot App.

7. Konklusjon

I denne veiledningen så vi på forskjellene mellom OpenAPI-baserte springdoc og Spring REST Docs. Vi så også hvordan vi implementerer de to for å generere dokumentasjon for et grunnleggende CRUD API.

Oppsummert, begge har sine fordeler og ulemper, og beslutningen om å bruke den ene over den andre er underlagt våre spesifikke krav.

Som alltid er kildekoden tilgjengelig på GitHub.

HVILLE bunnen

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

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