Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring REST Docs — Documenting RESTful APIs

Spring REST Docs — Documenting RESTful APIs

Slides of the talk I held at OOP 2016.

@springcentral

Oliver Drotbohm

February 03, 2016
Tweet

More Decks by Oliver Drotbohm

Other Decks in Programming

Transcript

  1. The writing experience is unpleasant @ApiOperation(value = "Create a new

    user", notes = "The name " + "of the user must be at least four characters in length.") @ApiResponses({ @ApiResponse(code = 400, message = "Bad request"), @ApiResponse(code = 201, message = "User created")}) @ResponseStatus(HttpStatus.CREATED) @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) public HttpHeaders create(@RequestBody UserInput userInput) { … }
  2. It isn’t dry @ApiResponses({ @ApiResponse(code=400, message="Bad request"), @ApiResponse(code=201, message="User created")})

    @ResponseStatus(HttpStatus.CREATED) public HttpHeaders create(@RequestBody UserInput userInput) { … } @ExceptionHandler(IllegalArgumentException.class) private ResponseEntity<Void> handleIllegalArgumentException() { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); }
  3. @ApiResponses({ @ApiResponse(code=400, message="Bad request"), @ApiResponse(code=201, message="User created")}) @ResponseStatus(HttpStatus.CREATED) public HttpHeaders

    create(@RequestBody UserInput userInput) { … } @ExceptionHandler(IllegalArgumentException.class) private ResponseEntity<Void> handleIllegalArgumentException() { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } It isn’t dry
  4. @ApiResponses({ @ApiResponse(code=400, message="Bad request"), @ApiResponse(code=201, message="User created")}) @ResponseStatus(HttpStatus.CREATED) public HttpHeaders

    create(@RequestBody UserInput userInput) { … } @ExceptionHandler(IllegalArgumentException.class) private ResponseEntity<Void> handleIllegalArgumentException() { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } It isn’t dry
  5. @ApiResponses({ @ApiResponse(code=400, message="Bad request"), @ApiResponse(code=201, message="User created")}) @ResponseStatus(HttpStatus.CREATED) public HttpHeaders

    create(@RequestBody UserInput userInput) { … } @ExceptionHandler(IllegalArgumentException.class) private ResponseEntity<Void> handleIllegalArgumentException() { return new ResponseEntity<>(HttpStatus.PAYMENT_REQUIRED); } It’s outright wrong
  6. "Code generation – so that you can do the wrong

    thing faster… Kevlin Henney
  7. Writing a test case… @Test public void index() throws Exception

    { this.mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(jsonPath("_links.notes", is(notNullValue()))) .andExpect(jsonPath("_links.tags", is(notNullValue()))) .andDo(document("index")); } 1 2 3 4
  8. … as well as a response one… [source, http] ----

    HTTP/1.1 200 OK Content-Type: application/hal+json { "_links" : { "tags" : { "href" : "http://localhost:8080/tags" }, "notes" : { "href" : "http://localhost:8080/notes" } } ----
  9. Document links and payloads / responses this.mockMvc.perform(get("/")) .andExpect(status().isOk()) .andDo(document("index", links(

    linkWithRel("notes").description( "The <<resources-notes, Notes resource>>"), linkWithRel("tags").description( "The <<resources-tags, Tags resource>>") ), responseFields( fieldWithPath("_links").description( "<<resources-index-links,Links>> to other resources") ) )); 1 2 3
  10. Document links and payloads / responses |=== | Relation |

    Description | notes | The <<resources-notes, Notes resource>> | tags | The <<resources-tags, Tags resource>> |===
  11. Document links and payloads / responses |=== |Path|Type|Description |_links |Object

    |<<resources-index-links,Links>> to other resources |===
  12. Resources Chatty42 — GitHub repository, App on Heroku Spring RESTDocs

    — GitHub repository Andy Wilkinson - Documenting RESTful APIs — Webinar recording