Slide 1

Slide 1 text

Wie soll man das denn nutzen? Dokumentation von REST-Schnittstellen mit Spring REST Docs Gerrit Meier@JUG Saxony Day 2016

Slide 2

Slide 2 text

„Eine schöne RESTful-API haben Sie da, wäre ja traurig, wenn die keiner nutzen 
 würde.“

Slide 3

Slide 3 text

Problem: keine Dokumentation vorhanden

Slide 4

Slide 4 text

Lösung: Dokumentation nachholen

Slide 5

Slide 5 text

https://www.flickr.com/photos/whateverjamesinstitches/4623922729/

Slide 6

Slide 6 text

Senior Consultant
 T-Systems on site services GmbH 8 Jahre ‚professionell‘ Java JUG Ostfalen Co-Organisator Podcast meistermeier

Slide 7

Slide 7 text

Gibt es da nicht schon etwas?

Slide 8

Slide 8 text

Swagger

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

@ApiOperation(nickname = "loginUser",
 value = "Logs user into the system", response = classOf[String], 
 httpMethod = "GET")
 @ApiResponses(Array(
 new ApiResponse(code = 400, message = "Invalid username and password combination")))
 def loginUser(
 @ApiParam(value = "The user name for login", required = true)
 username: String,
 @ApiParam(value = "The password for login in clear text", required = true) 
 password: String) = Action { 
 implicit request =>
 JsonResponse("logged in user session:" + System.currentTimeMillis())
 }

Slide 11

Slide 11 text


 @ApiOperation(nickname = "updateUser",
 value = "Updated user“, notes = "This can only be done by the logged in user.", httpMethod = "PUT")
 @ApiResponses(Array(
 new ApiResponse(code = 400, message = "Invalid username supplied"),
 new ApiResponse(code = 404, message = "User not found")))
 @ApiImplicitParams(Array(
 new ApiImplicitParam (name = "username", value = "name that need to be 
 updated", required = true, dataType = "String", paramType = "path"),
 new ApiImplicitParam(name = "body", value = "Updated user object",
 required = true, dataType = "models.User", paramType = "body")))
 def updateUser(username: String) = Action { implicit request =>
 request.body.asJson match {
 case Some(e) => {
 val user = Json.mapper.readValue(e.toString, classOf[User])
 userData.addUser(user)
 JsonResponse(user)
 }
 case None => JsonResponse(new value.ApiResponse(400, "Invalid input"))
 }
 }

Slide 12

Slide 12 text

https://www.flickr.com/photos/endless_autumn/2820524593

Slide 13

Slide 13 text

Das Problem von Dokumentation am 
 Code ist, dass sie veralten wird.

Slide 14

Slide 14 text

@ApiResponses(
 value = {
 @ApiResponse(code = 200, message = "leaking resource available"),
 @ApiResponse(code = 404, message = "cannot find leaking resource"),
 }
 )
 public Status getLeakingInformation() {
 return new Status(200);
 }

Slide 15

Slide 15 text

@ApiResponses(
 value = {
 @ApiResponse(code = 200, message = "leaking resource available"),
 @ApiResponse(code = 404, message = "cannot find leaking resource"),
 }
 )
 public Status getLeakingInformation() {
 return new Status(451);
 }

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

http://martinfowler.com/articles/richardsonMaturityModel.html The Swamp of POX Resources HTTP Verbs Hypermedia Controls

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Spring REST Docs

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

{
 "_links" : {
 "breweries" : {
 "href" : "http://localhost:8888/breweries"
 },
 "beers" : {
 "href" : "http://localhost:8888/beers"
 },
 "profile" : {
 "href" : "http://localhost:8888/profile"
 }
 }
 } $ curl 'http://localhost:8080/' -i

Slide 22

Slide 22 text

@Test
 public void index() throws Exception {
 mockMvc.perform(RestDocumentationRequestBuilders.get(„/")) .andExpect(MockMvcResultMatchers.status().isOk())
 .andDo( document("index-page", links(
 linkWithRel("breweries").
 description("The <>“),
 linkWithRel("beers").description("The <>"),
 linkWithRel("profile").description("The ALPS profile for the service")
 ), responseFields( fieldWithPath("_links")
 .description("<> to other resources“) )
 ));
 }

Slide 23

Slide 23 text

@Test
 public void index() throws Exception {
 mockMvc.perform(RestDocumentationRequestBuilders.get(„/")) .andExpect(MockMvcResultMatchers.status().isOk())
 .andDo( document("index-page", links(
 linkWithRel("breweries").
 description("The <>“),
 linkWithRel("beers").description("The <>"),
 linkWithRel("profile").description("The ALPS profile for the service")
 ), responseFields( fieldWithPath("_links")
 .description("<> to other resources“) )
 ));
 }

Slide 24

Slide 24 text

@Test
 public void index() throws Exception {
 mockMvc.perform(RestDocumentationRequestBuilders.get(„/")) .andExpect(MockMvcResultMatchers.status().isOk())
 .andDo( document("index-page", links(
 linkWithRel("breweries").
 description("The <>“),
 linkWithRel("beers").description("The <>"),
 linkWithRel("profile").description("The ALPS profile for the service")
 ), responseFields( fieldWithPath("_links")
 .description("<> to other resources“) )
 ));
 }

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

include::

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

DEMO

Slide 35

Slide 35 text

meistermeier https://projects.spring.io/spring-restdocs/ https://github.com/spring-projects/spring-restdocs https://github.com/meistermeier/spring-rest-docs-sample