Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the...
Transcript of Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the...
![Page 1: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/1.jpg)
Spring REST DocsDocumenting RESTful services
![Page 2: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/2.jpg)
About me
@jeroenvschagen
• Senior developer at 42.nl • Open source • Lead product developer • Academic domain: VU, WUR
![Page 3: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/3.jpg)
API docs
![Page 4: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/4.jpg)
Needs to be done..
![Page 5: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/5.jpg)
Accuracy
• Otherwise nobody will read it
![Page 6: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/6.jpg)
Efficiency
• Use a tool designed for writing
• Generate when possible
• No duplication
• Cross cutting concerns
![Page 7: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/7.jpg)
Swagger
![Page 8: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/8.jpg)
Swagger
• Describes the RESTful API: /apidocs
• Analysing the implementation
• Spring MVC
• Jersey
![Page 9: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/9.jpg)
Swagger@RestController @RequestMapping(value = "/books") public class BooksController { @RequestMapping(value = "/{id}", method = RequestMethod.GET) public Book findById(@PathVariable Long id) { return bookService.findById(id); } @RequestMapping(method = RequestMethod.POST) public Book save(@RequestBody Book book) { return bookService.save(book); } @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public Book delete(@PathVariable Long id) { return bookService.delete(id); } ... }
![Page 10: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/10.jpg)
Swagger UI
![Page 11: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/11.jpg)
![Page 12: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/12.jpg)
Not quite there yet...
![Page 13: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/13.jpg)
Hypermedia
![Page 14: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/14.jpg)
http://martinfowler.com/articles/richardsonMaturityModel.html
![Page 15: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/15.jpg)
Spring HATEAOS{ "_embedded" : { "product" : [ { "name" : "My product", "_links" : { "self" : { "href" : "http://localhost:8080/products/1" },
"vendors": { "href" : "http://localhost:8080/products/1/vendors" }
} } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/products" } }}
http://localhost:8080/products
Hypermedia
Data
![Page 16: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/16.jpg)
![Page 17: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/17.jpg)
URI vs Resource based
![Page 18: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/18.jpg)
URI vs Resource based
![Page 19: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/19.jpg)
![Page 20: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/20.jpg)
Buggy
Frameworks are complicated
Interceptor, Filter, ControllerAdvice, ArgumentResolver,
ResultHandler, Converters
![Page 21: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/21.jpg)
Customisation @ApiOperation(value = "find-all", notes = "Retrieves all system users.") @ApiResponses({ @ApiResponse(code = 200, message = "Everything goes ok."), @ApiResponse(code = 403, message = "Not allowed to retrieve data."), @ApiResponse(code = 500, message = "Unexpected server error.") }) @ResponseBody @RequestMapping(method = GET) public Iterable<User> findAll(Principal principal) { return userService.findAll(Users.getUserName(principal)); }
Annotation hell
Duplication (code, annotation)
@ApiModel(value = "CreateUserForm", description = "Form for creating a user") public class CreateUserForm { @NotNull @ApiModelProperty(value = "email", required = true) private String email; }
![Page 22: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/22.jpg)
Cross cutting concerns
• HTTP verbs, status codes
• Authentication, versioning
• Error handling
• Duplication...
![Page 23: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/23.jpg)
Production
• Security risk
• Only for authorised users
• Disable /api-docs
• Framework size
![Page 24: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/24.jpg)
Instant try
![Page 25: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/25.jpg)
Alternatives?
![Page 26: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/26.jpg)
Swagger2Markup
![Page 27: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/27.jpg)
Spring REST Docs
![Page 28: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/28.jpg)
Spring project
• Documenting RESTful services
• Andy Wilkinson (Pivotal)
• 1.0 release in October 20151.1 released May 31, 2016
• Webinar: Documenting RESTful APIshttps://www.youtube.com/watch?v=knH5ihPNiUs
![Page 29: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/29.jpg)
Write tests
Generate snippets
Add handwritten content
User guide
(Frequently changing)
(Rarely changing)
![Page 30: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/30.jpg)
Test driven documentation
• Encourages testing
• No annotations / duplication in code
• Realistic examples
• Documentation is accurate, or the test fails
![Page 31: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/31.jpg)
![Page 32: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/32.jpg)
Generate snippetsSpring test
ResultHandler (org.springframework.restdocs.mockmvc.MockMvcRestDocumentation)
this.webClient.perform(get("/users/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.email").value("[email protected]")) .andDo(document("user-find-by-id"));
![Page 33: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/33.jpg)
Generate snippets
![Page 34: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/34.jpg)
Generate snippetscurl-request.adoc
http-request.adoc
request-fields.adoc
http-response.adoc
response-fields.adoc
links.adoc
![Page 35: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/35.jpg)
Generate snippets
[source,http]----HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Content-Length: 40
{ "id" : 2, "email" : "[email protected]"}----
http-response.adoc
![Page 36: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/36.jpg)
Start writingAsciidoctor
![Page 37: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/37.jpg)
Include contentAsciidoctor
![Page 38: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/38.jpg)
Document
![Page 39: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/39.jpg)
Alternatives (1.1.0+)
• JUnit / TestNG
• MockMVC / REST Assured
• Asciidoctor / Markdown
![Page 40: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/40.jpg)
Let's code
![Page 41: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/41.jpg)
Spring boot<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId></dependency>
![Page 42: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/42.jpg)
Application
@SpringBootApplicationpublic class SampleApplication { public static void main(String[] args) throws Exception { SpringApplication.run(SampleApplication.class, args); }
}
![Page 43: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/43.jpg)
JPA Entity
@Entitypublic class User extends BaseEntity { private String email;
public String getEmail() { return email; } public void setEmail(String email) { this.email = email; }
}
![Page 44: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/44.jpg)
Controller
@RestController@RequestMapping("/users")public class UserController { private final UserRepository userRepository; @Autowired public UserController(UserRepository userRepository) { this.userRepository = userRepository; }
@RequestMapping(method = RequestMethod.GET) public Iterable<User> findAll() { return userRepository.findAll(); } ... }
![Page 45: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/45.jpg)
+ Spring REST docs
![Page 46: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/46.jpg)
Add dependency
<dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-mockmvc</artifactId> <scope>test</scope></dependency>
![Page 47: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/47.jpg)
Spring test@WebAppConfigurationpublic abstract class AbstractWebIntegrationTest extends AbstractIntegrationTest { @Rule public final JunitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets"); @Autowired private WebApplicationContext webApplicationContext; protected MockMvc webClient; @Before public void initWebClient() { this.webClient = MockMvcBuilders.webAppContextSetup(webApplicationContext) .apply(MockMvcRestDocumentation.documentationConfiguration(this.restDocumentation)) .alwaysDo(print()) .build(); }
}
MockMvcConfigurer (org.springframework.restdocs.mockmvc.RestDocumentationMockMvcConfigurer)
![Page 48: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/48.jpg)
Spring test
public class UserControllerTest extends AbstractWebIntegrationTest {
@Autowired private UserRepository userRepository; @Test public void testFindById() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);
this.webClient.perform(get("/users/" + user.getId())) .andExpect(status().isOk()) .andExpect(jsonPath("email").value("[email protected]")) .andDo(document("user-find-by-id", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("id").description("The user's identifier."), fieldWithPath("email").description("The user's email address.")))); } }
![Page 49: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/49.jpg)
Spring test
[source,http]----HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Content-Length: 40
{ "id" : 2, "email" : "[email protected]"}----
[source,bash]----$ curl 'http://localhost:8080/users/2' -i----
|===|Path|Type|Description
|id|Number|The user's identifier.
|email|String|The user's email address.
|===
curl-request.adoc
http-response.adoc
response-fields.adoc
target/generated-snippets
![Page 50: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/50.jpg)
Asciidoctor plugin<plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>1.5.2</version> <executions> <execution> <id>generate-docs</id> <phase>package</phase> <goals> <goal>process-asciidoc</goal> </goals> <configuration> <backend>html</backend> <doctype>book</doctype> <sourceDocumentName>index.adoc</sourceDocumentName> <attributes> <generated>${project.build.directory}/generated-doc</generated> </attributes> </configuration> </execution> </executions></plugin>
![Page 51: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/51.jpg)
Manual docs
:toc: left
= Sample REST APIJeroen van Schagen
[[abstract]]
Generated documentation using Spring REST Docs.
include::chapters/general.adoc[]include::chapters/resources.adoc[]
:snippets: ../../../../../target/generated-snippets
== Users
Users are used to authenticate with the system.
=== Get one user
Retrieves a user based on identifier.
Request
include::{snippets}/user-find-by-id/curl-request.adoc[]
Response
include::{snippets}/user-find-by-id/http-response.adoc[]include::{snippets}/user-find-by-id/response-fields.adoc[]
![Page 52: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/52.jpg)
![Page 53: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/53.jpg)
Publish
Github pages
https://github.com/github/maven-plugins
Nexus assembly
![Page 54: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/54.jpg)
Github
https://developer.github.com/v3/
![Page 55: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/55.jpg)
More requests..
![Page 56: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/56.jpg)
GET List
@Test public void testFindAll() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);
this.webClient.perform(get("/users")) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].email").value("[email protected]")) .andDo(document("user-find-all", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("[].id").description("The user's identifier."), fieldWithPath("[].email").description("The user's email address.")))); }
Arrays
![Page 57: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/57.jpg)
POST
@Test public void testSave() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);
this.webClient.perform(post("/users") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(user))) .andExpect(status().isOk()) .andExpect(jsonPath("$.email").value("[email protected]")) .andDo(document("user-save", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), requestFields( fieldWithPath("id").description("The user's identifier."), fieldWithPath("email").description("The user's email address.")), responseFields( fieldWithPath("id").description("The user's identifier."), fieldWithPath("email").description("The user's email address.")))); }
![Page 58: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/58.jpg)
DELETE
@Test public void testDelete() throws Exception { final User user = new User(); user.setEmail("[email protected]"); userRepository.save(user);
this.webClient.perform(delete("/users/" + user.getId())) .andExpect(status().isOk()) .andDo(document("user-delete", preprocessResponse(prettyPrint()))); }
![Page 59: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/59.jpg)
Error handling
![Page 60: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/60.jpg)
@ControllerAdvicepublic class ControllerExceptionAdvice {
private static final Logger LOGGER = LoggerFactory.getLogger(ControllerExceptionAdvice.class);
@ExceptionHandler({ Exception.class }) public ModelAndView handleOther(HttpServletResponse response, Object handler, Exception ex) { LOGGER.error("Handling request, for [" + handler + "], resulted in the following exception.", ex); response.setStatus(INTERNAL_SERVER_ERROR.value()); return new ModelAndView(new MappingJackson2JsonView(), "error", new ExceptionJsonBody(ex)); }
public static class ExceptionJsonBody { private final Class<?> type; private final String message;
public ExceptionJsonBody(Exception ex) { this.type = ex.getClass(); this.message = ex.getMessage(); }
public Class<?> getType() { return type; } public String getMessage() { return message; }
}
}
![Page 61: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/61.jpg)
Error handling
@Test public void testDeleteUnknown() throws Exception { this.webClient.perform(delete("/users/42")) .andExpect(status().isInternalServerError()) .andExpect(jsonPath("$.error.message").value("User with id 42 does not exist.")) .andDo(document("user-delete-unknown", preprocessResponse(prettyPrint()), responseFields( fieldWithPath("error.type").description("The type of error."), fieldWithPath("error.message").description("The error message.")))); }
![Page 62: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/62.jpg)
Spring Data REST@RepositoryRestResource(collectionResourceRel = "product", path = "products")public interface ProductRepository extends CrudRepository<Product, Long> { }
{ "_embedded" : { "product" : [ { "name" : "My product", "_links" : { "self" : { "href" : "http://localhost:8080/products/1" }, "product" : { "href" : "http://localhost:8080/products/1" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/products" }, "profile" : { "href" : "http://localhost:8080/profile/products" } }}
/products
Hypermedia
![Page 63: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/63.jpg)
Spring Data REST @Test public void testFindAll() throws Exception { final Product product = new Product(); product.setName("My product"); productRepository.save(product);
this.webClient.perform(get("/products")) .andExpect(status().isOk()) .andExpect(jsonPath("_embedded.product[0].name").value("My product")) .andExpect(jsonPath("_links.self", is(notNullValue()))) .andExpect(jsonPath("_links.profile", is(notNullValue()))) .andDo(document("product-find-all", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("_embedded.product[].name").description("The product name."), fieldWithPath("_embedded.product[]._links").description("Links related to this product."), fieldWithPath("_links").description("Links related to products in general.")), links( linkWithRel("self").description("Reference to self."), linkWithRel("profile").description("Shows the product profile.")))); }
Hypermedia
![Page 64: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/64.jpg)
Reusing snippets (1.1.0+) @Test public void testFindAll() throws Exception { final Product product = new Product(); product.setName("My product"); productRepository.save(product);
this.webClient.perform(get("/products")) .andExpect(status().isOk()) .andExpect(jsonPath("_embedded.product[0].name").value("My product")) .andExpect(jsonPath("_links.self", is(notNullValue()))) .andExpect(jsonPath("_links.profile", is(notNullValue()))) .andDo(document("product-find-all", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("_embedded.product[].name").description("The product name."), fieldWithPath("_embedded.product[]._links").description("Links related to this product."), fieldWithPath("_links").description("Links related to products in general.")), links( this.pageLinks.and( linkWithRel("profile").description("Shows the product profile.") )))); }
Reused
![Page 65: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/65.jpg)
Demo
![Page 66: Spring REST Docs - Meetupfiles.meetup.com/19071756/spring-rest-docs.pdfSwagger • Describes the RESTful API: /apidocs • Analysing the implementation • Spring MVC • Jersey](https://reader034.fdocuments.net/reader034/viewer/2022051410/602ce063a6cd3666a8349ed9/html5/thumbnails/66.jpg)
Questions?