Hallo Spaß-Coder.
Das agile Manifesto[1] besagt unter anderem „Funktionierende Software mehr als umfassende Dokumentation„. Damit ist jedoch nicht gemeint, dass ein Softwareprojekt keine Dokumentation enthalten sollte. Wer hat selbst schon einmal versucht eine API zu verwenden und sich dann mühsam durch Versuch und Irrtum sehr zäh dem gewünschten Ergebnis genähert? Oder eine fünf Jahre alte Dokumentation zu Hilfe genommen, weil es keine aktuelle Ausführung gibt? Anders als bei Wein wird eine Doku durch lange Lagerung ohne Einfluss von Außen nicht besser mit der Zeit.
In diesem Artikel schauen wir uns verschiedene Dokumentationswerkzeuge an. Das Ziel ist jeweils, die API zu dokumentieren und nicht das gesamte Projekt (wie etwa mit Javadoc[2]). Wir stellen vier verschiedene Werkzeuge für die Dokumentation einer REST-API in Java vor und zeigen am Ende des Artikels einen Vergleich der Werkzeuge auf.
Spring REST Doc[3]
Wie der Name bereits vermuten lässt, handelt es sich bei Spring REST Doc um eine Erweiterung von Spring. Die Idee dabei ist, mit Hilfe der API-Tests Dokumentationsschnipsel zu generieren, welche per AsciiDoctor oder Markdown zu einer Doku konsolidiert und gerendert werden. Die Tests werden hierbei mit Spring MVC Test (via. MockMvc) oder REST Assured geschrieben.
Ein Test sieht mit Spring MVC Test dann so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@Test public void getPerson() throws Exception { this.mockMvc.perform(get("/persons/{id}", this.personOne.getId())) .andExpect(status().isOk()) .andExpect(content().contentType(MediaTypes.HAL_JSON)) .andExpect(jsonPath("$.person.name", is(this.personOne.getName()))) .andExpect(jsonPath("$.person.firstname", is(this.personOne.getFirstname()))) .andDo(this.document.snippets( pathParameters( parameterWithName("id").description("The id to find the person with.") ), responseFields( fieldWithPath("person.id").description("The person's id"), fieldWithPath("person.name").description("The person's name"), fieldWithPath("person.firstname").description("The person's first name"), fieldWithPath("_links").description("Links to this resource") ), links(halLinks(), linkWithRel("self").description("Link to this resource.") ) )); } |
Bei Ausführung der Test werden nach der Konfiguration von Maven oder Gradle verschiedene Textschnipsel erzeugt, z.B. für die durchgeführte Anfrage und die Antwort. Diese Schnipsel werden über AsciiDoc (adoc) Dateien dann zu einer Dokumentation zusammengefasst, welche z.B. als HTML gerendert wird.
Weitere Beispiele sind zu finden unter: https://github.com/spring-projects/spring-restdocs/tree/master/samples
JSONDoc[4]
JSONDoc verfolgt einen sehr geradlinigen Ansatz bei der Erstellung der Dokumentation. So werden die notwendigen Annotationen für die Dokumentation direkt an die Controller-Methoden und den DTO-Objekten geschrieben. Was auf den ersten Blick als Überfrachtung des Live-Codes aussieht wirkt nach kurzer Eingewöhnung wie aus einem Guss. Die Dokumentation passt zum Code und hält alle Details zur API an einem Ort vorrätig.
Durch Erweiterungsmodule für Spring MVC und Spring Boot wird ein großer Teil der Dokumentation bereits aus den Spring-Annotationen generiert. So müssen nur noch weiterführende Informationen in den speziellen JSONDoc Annotationen geschrieben werden.
Eine, mithilfe von JSONDoc beschriebene, Spring MVC basierte Controller-Methode kann etwa folgendermaßen aussehen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
@Api(name = "Person Service", description = "This service provides methods to fetch information about Persons.", group = "Master Data", visibility = ApiVisibility.PUBLIC, stage = ApiStage.GA) @ApiVersion(since = "V1") @ApiAuthToken @RestController @RequestMapping("api/v1/persons") public class PersonApiController { private final PersonRepository personRepository; private final PersonApiAssembler personApiAssembler; @Inject public PersonApiController(PersonRepository personRepository, PersonApiAssembler personApiAssembler) { this.personRepository = personRepository; this.personApiAssembler = personApiAssembler; } @ApiMethod(description = "Returns the Person with the given id.") @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public PersonApiDto getPersonAsJson(@ApiPathParam(name = "id", description = "Id of the person to look up.") @PathVariable UUID id) { Person person = personRepository.findById(id); if(person == null){ throw new ResourceNotFoundException(); } return personApiAssembler.toDto(person); } } |
Über eine konfigurierte Seite ist von der Applikation nun eine entsprechende Dokumentation als JSON abrufbar. Das JSONDoc-Projekt bietet jsondoc-ui dafür eine einfache und komfortable Möglichkeit, diese interaktiv im Browser einzusehen. Dazu wird das JSON eingelesen und mit Hilfe von Javascript interpretiert. Die resultierende Seite über das separate jsondoc-ui sieht in etwa folgendermaßen aus:
Die übertragenen Daten werden typischerweise in DTOs verpackt, JSONDoc nennt diese Objekte (Objects). Ein DTO welches mit Hilfe von JSONDoc beschrieben ist, hat im Code folgenden Aufbau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@ApiObject(name = "Person", description = "A Person which stands in any relation to the company and/or any " + "organisational units managed by the application.", group = "Master Data", visibility = ApiVisibility.PUBLIC, stage = ApiStage.GA) public class PersonApiDto { @ApiObjectField(description = "Unique id of the Person") public UUID id; @ApiObjectField(description = "Shows the date of birth of the Person.", format = "YYYY-MM-DD") public LocalDateTime dateOfBirth; @ApiObjectField(description = "First name") public String firstName; @ApiObjectField(description = "Last name") public String lastName; @ApiObjectField(description = "User name of the Person") public String userName; @ApiObjectField(description = "Gender of the Person. Allowed values: " + "<b>M</b>ale, <b>F</b>emale, <b>N</b>eutral", allowedvalues = {"M", "F", "N"}) public String gender; @ApiObjectField(description = "Business contact information of the Person") public BusinessContactApiDto businessContact; @ApiObjectField(description = "Flag whether the Person is active or not.") public boolean active; } |
Aussehen wird das Ganze in der jsondoc-ui hinterher wie im folgenden Bild angegeben. Dabei werden die unterschiedlichen Objekte allesamt angezeigt. Über den Type eines Werts wird deutlich, welches Objekt sich dahinter verbirgt. Die entsprechende Dokumentation kann dann ebenfalls interaktiv aufgerufen werden.
Ein eigenes Maven-Modul (jsondoc-maven-plugin) hilft bei der Generierung einer Offline-Dokumentatio. Dies ist notwendig, wenn dem Entwickler, der die API verwenden möchte, die Anwendung selbst nicht zur Verfügung steht und der Interaktive Abruf damit nicht möglich ist. Bei der Offline-Version wird das Ergebnis des JSON-Exports von JSONDoc in die HTML-Seite von jsondoc-ui reingeneriert.
Swagger[5]
Swagger kann grundsätzlich in allen Java-Projekten genutzt werden. Dennoch schauen wir uns in diesem Artikel nur die spezifische Implementierung für Spring-Projekte an. Die Verwendung in Nicht-Spring-Projekten ist sehr ähnlich.
SpringFox[6]
Spring Fox (ehemals swagger-springmvc) bietet die Integration von Swagger für Spring und Spring Boot Projekte. Mit springfox-staticdocs kann zusätzlich eine statische Dokumentation wie bei Spring REST Doc generiert werden, womit hier zwei Ansätze kombiniert werden können. Zusätzlich verringert Spring Fox die Notwendigkeit von Swagger-Annotationen zur Dokumentation im Produktionscode, da die vorhandenen Spring-Annotationen ausgewertet werden. Zusätzlich können Swagger-Annotationen genutzt werden, um Abweichungen und zusätzlich Informationen zu dokumentieren, was in den Entitäten sowie im Controller angebracht ist.
SpringFox bringt auch gleich ein entsprechendes UI mit, so dass sich jeder Service selbst dokumentieren kann und die Dokumentation einheitlich auf immer demselben Endpunkt zu finden ist.
Auf Grund der sehr umfangreichen und ausführlichen Dokumentation verzichten wir an dieser Stelle auf zusätzliche Beispiele.
Vergleich der Tools
Nach der Vorstellung der grundsätzliche Funktionsweise stellen wir hier einen Vergleich der Werkzeuge an.
Werkzeug | Wo schreibe ich die Doku? | Welche Ausgaben werden erzeugt? | Für welchen Einsatzzweck eignet sich der Ansatz? | Besonderheiten |
Spring REST Doc | Die Beschreibung erfolgt über die API-Test. Damit bleibt der Produktionscode unberührt. Zusätzlich werden manuell adoc-Dateien geschrieben. | – HTML5 – DocBook – man |
– statische Dokumentation – mehr als nur API |
Tests schlagen fehl, wenn Dokumentation unvollständig ist
kombiniert manuell geschriebene Doku und generierte Doku für Spring / Spring Boot Projekte |
JSONDoc | Die Beschreibung erfolgt über Annotationen in den Controllern des Produktionscodes. | – JSON | – interaktive Dokumentation – beschreibt nur API – offline Dokumentation |
Entwickler können vor der Nutzung der API diese interaktiv ausprobieren und damit Anfragen und Antworten prüfen (in separatem Ausgabemodul)
offline Dokumentation möglich (via Maven) Doku als HTML-Seiten mit Bootstrap-Theme Erweiterung für Spring / Spring Boot Projekte möglich |
Swagger | Die Beschreibung erfolgt über Annotationen in den Controllern des Produktionscodes. | – JSON – YAML |
– interaktive Dokumentation – beschreibt nur API |
Entwickler können vor der Nutzung der API diese interaktiv ausprobieren und damit Anfragen und Antworten prüfen
vollständig generierte Doku für Java Projekte |
SpringFox | Überwiegend wird die Dokumentation aus den bereits vorhandenen Spring-Annotationen generiert. Swagger Annotation sind zusätzlich möglich. | – JSON (swagger) – YAML (swagger) – adoc (AsciiDoctor) |
– statische und interaktive Dokumentation | reduziert die Notwendigkeit von Annotationen für Doku
kombiniert manuell geschriebene Doku und generierte Doku für Spring / Spring Boot Projekte |
Zusammenfassung
Warum schreibt ihr eine API? Vermutlich damit sie jemand (anderes) verwendet und einen Nutzen aus dieser Verwendung generiert. Wie soll jedoch eure API verwendet werden, wenn nicht klar ist, wie sie funktioniert. Wie haben uns in diesem Artikel vier verschiedenen Werkzeuge angeschaut, um im Umfeld von Java-Projekten eine REST-API für andere gut lesbar oder sogar erfahrbar zu dokumentieren.
Die Seiten der jeweiligen Projekte bieten sehr detaillierte und umfangreiche Dokumentationen mit reichlich Beispielen, wie sie angewendet werden können. Aus diesem Grund haben wir auf eigene Implementierungsbeispiele verzichtet.
Wir hoffen euch ein wenig bei der Auswahl des geeigneten Werkzeugs unterstützt zu haben. Setzt ihr vielleicht noch andere Tools ein, die hier nicht auftauchen, aber viiieeel besser sind? Dann lasst es uns wissen.
Eure Spaß-Coder
Dieser Artikel basiert neben unseren Erfahrungen auf den Ausführungen aus:
- [1] http://www.agilemanifesto.org/iso/de/
- [2] http://www.java-doc.de/
- [3] http://docs.spring.io/spring-restdocs/docs/current/reference/html5/
- [4] http://jsondoc.org/
- [5] http://swagger.io/
- [6] http://springfox.github.io/springfox/