Въведение в пролетните REST документи

ПОЧИВКА Най-горе

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА

1. Общ преглед

Spring REST Docs генерира документация за RESTful услуги, която е едновременно точна и четлива. Той съчетава ръчно написана документация с автоматично генерирани фрагменти на документи, създадени с тестове Spring.

2. Предимства

Една от основните философии на проекта е използването на тестове за изготвяне на документацията. Това гарантира, че винаги генерираната документация съвпада точно с действителното поведение на API. Освен това изходът е готов за обработка от Asciidoctor, издателска верига от инструменти, центрирана около синтаксиса AsciiDoc. Това е същият инструмент, който се използва за генериране на документацията на Spring Framework.

Тези подходи намаляват ограниченията, наложени от други рамки. Spring REST Docs създава точна, кратка и добре структурирана документация. След това тази документация позволява на потребителите на уеб услуги да получат необходимата им информация с минимум суетене.

Инструментът има някои други предимства, като например:

  • генерират се фрагменти с искане за curl и http
  • лесна за пакетиране на документация в файл jar
  • лесно е да добавите допълнителна информация към фрагментите
  • поддържа както JSON, така и XML

Тестовете, които произвеждат фрагменти, могат да бъдат написани с помощта на поддръжка на Spring MVC Test, WebTestClient на Spring Webflux или REST-Assured.

В нашите примери ще използваме Spring MVC тестове, но използването на другите рамки е много подобно.

3. Зависимости

Идеалният начин да започнете да използвате Spring REST Docs в проект е като използвате система за управление на зависимости. Тук използваме Maven като инструмент за изграждане, така че зависимостта по-долу може да бъде копирана и поставена във вашия POM:

 org.springframework.restdocs spring-restdocs-mockmvc 2.0.4.RELEASE 

Можете също да проверите Maven Central за нова версия на зависимостта тук.

В нашия пример се нуждаем от зависимостта spring-restdocs-mockmvc, тъй като използваме поддръжката на Spring MVC за създаване на нашите тестове.

Ако искаме да напишем тестове с помощта на WebTestClient или REST Assured, ще ни трябват зависимостите spring-restdocs-webtestclient и spring-restdocs-restassured.

4. Конфигурация

Както споменахме, ще използваме Spring MVC Test framework, за да отправяме заявки към REST услугите, които трябва да бъдат документирани. Изпълнението на теста създава фрагменти на документация за заявката и получения отговор.

Можем да използваме библиотеката както с тестове JUnit 4, така и с JUnit 5. Нека видим конфигурацията, необходима за всеки.

4.1. JUnit 4 Конфигурация

Първата стъпка в генерирането на фрагменти на документация за тестове на JUnit 4 е да се декларира публично поле JUnitRestDocumentation, което е анотирано като JUnit @Rule .

В JUnitRestDocumentation правилото е конфигуриран с изходната директория, в която генерираните фрагменти трябва да бъдат спасени. Например, тази директория може да бъде директорията за изграждане на Maven:

@Rule public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");

След това настроихме контекста на MockMvc, така че той да бъде конфигуриран да създава документация:

@Autowired private WebApplicationContext context; private MockMvc mockMvc; @Before public void setUp(){ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(documentationConfiguration(this.restDocumentation)) .build(); }

Обектът MockMvc е конфигуриран с помощта на MockMvc RestDocumentationConfigurer . Екземпляр на този клас може да бъде получен от метода static staticConfiguration () на org.springframework.restdocs.mockmvc.MockMvcRestDocumentation .

4.2. JUnit 5 Конфигурация

За да работим с тест JUnit 5, трябва да разширим теста с класа RestDocumentationExtension :

@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) @SpringBootTest public class ApiDocumentationJUnit5IntegrationTest { //... }

Този клас се конфигурира автоматично с изходна директория / target / generated-snippets, когато се използва Maven, или / build / generate-snippets за Gradle.

След това трябва да настроим екземпляра MockMvc в метод @BeforeEach :

@BeforeEach public void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) { this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) .apply(documentationConfiguration(restDocumentation)).build(); }

Ако не използваме JUnit за тестовете, тогава трябва да използваме класа ManualRestDocumentation .

5. RESTful услуга

Нека създадем CRUD RESTful услуга, която можем да документираме:

@RestController @RequestMapping("/crud") public class CRUDController { @GetMapping public List read(@RequestBody CrudInput crudInput) { List returnList = new ArrayList(); returnList.add(crudInput); return returnList; } @ResponseStatus(HttpStatus.CREATED) @PostMapping public HttpHeaders save(@RequestBody CrudInput crudInput) { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setLocation( linkTo(CRUDController.class).slash(crudInput.getTitle()).toUri()); return httpHeaders; } @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id) { // delete } }

След това нека добавим и IndexController, който връща страница с връзка към основната крайна точка на CRUDController :

@RestController @RequestMapping("/") public class IndexController { static class CustomRepresentationModel extends RepresentationModel { public CustomRepresentationModel(Link initialLink) { super(initialLink); } } @GetMapping public CustomRepresentationModel index() { return new CustomRepresentationModel(linkTo(CRUDController.class).withRel("crud")); } }

6. JUnit тестове

Обратно в тестовете можем да използваме екземпляра MockMvc, за да се обадим на нашите услуги и да документираме заявката и отговора.

First, to make sure every MockMvc call is automatically documented without any further configuration we can use the alwaysDo() method:

this.mockMvc = MockMvcBuilders //... .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()))) .build();

This set up ensures that every for every MockMvc call, the default snippets are created in a folder with the test method's name. Also, applying the prettyPrint() pre-processor displays the snippets in a more easily-readable manner.

Let's continue with customizing some of our calls.

To document our index page which contains a link, we can use the static links() method:

@Test public void indexExample() throws Exception { this.mockMvc.perform(get("/")).andExpect(status().isOk()) .andDo(document("index", links(linkWithRel("crud").description("The CRUD resource")), responseFields(subsectionWithPath("_links") .description("Links to other resources")) responseHeaders(headerWithName("Content-Type") .description("The Content-Type of the payload")))); }

Here, we're using the linkWithRel() method to document a link to /crud.

To add a Content-Type header to the response we're documenting it using the headerWithName() method and adding it to the responseHeaders() method.

We're also documenting the response payload using the responseFields() method. This can be used to document a more complex subsection of the response or a single field using the subsectionWithPath() or fieldWithPath() methods.

Similar to the response payload, we can also document the request payload using requestPayload():

@Test public void crudCreateExample() throws Exception { Map crud = new HashMap(); crud.put("title", "Sample Model"); crud.put("body", "//www.baeldung.com/"); this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON) .content(this.objectMapper.writeValueAsString(crud))) .andExpect(status().isCreated()) .andDo(document("create-crud-example", requestFields(fieldWithPath("id").description("The id of the input"), fieldWithPath("title").description("The title of the input"), fieldWithPath("body").description("The body of the input"), )))); }

In this example, we've documented our POST request that receives a CrudInput model with title and body fields and sends a CREATED status. Each field is documented using the fieldWithPath() method.

To document request and path parameter, we can use the requestParameters() and pathParameters() methods. Both methods use a parameterWithName() method to describe each parameter:

@Test public void crudDeleteExample() throws Exception { this.mockMvc.perform(delete("/crud/{id}", 10)).andExpect(status().isOk()) .andDo(document("crud-delete-example", pathParameters( parameterWithName("id").description("The id of the input to delete") ))); }

Here, we've documented our delete endpoint which receives an id path parameter.

The Spring REST Docs project contains even more powerful documentation functionalities, such as field constraints and request parts that can be found in the documentation.

7. Output

Once the build runs successfully, the output of the REST docs snippets will be generated and will be saved to the target/generated-snippets folder:

The generated output will have the information about the service, how to call the REST service like ‘curl' calls, the HTTP request and response from the REST service, and links/endpoints to the service:

CURL Command

---- $ curl '//localhost:8080/' -i ----

HTTP – REST Response

[source,http,options="nowrap"] ---- HTTP/1.1 200 OK Content-Type: application/hal+json;charset=UTF-8 Content-Length: 93 { "_links" : { "crud" : { "href" : "//localhost:8080/crud" } } } ----

8. Using Snippets to Create Documentation

To use the snippets in a larger document, you can reference them using Asciidoc includes. In our case, we have created a document in src/docs called api-guide.adoc:

In that document, if we wished to reference the links snippet, we can include it, using a placeholder {snippets} that will be replaced by Maven when it processes the document:

==== Links include::{snippets}/index-example/links.adoc[]

9. Asciidocs Maven Plugins

To convert the API guide from Asciidoc to a readable format, we can add a Maven plugin to the build lifecycle. There are several steps to enable this:

  1. Apply the Asciidoctor plugin to the pom.xml
  2. Add a dependency on spring-restdocs-mockmvc in the testCompile configuration as mentioned in the dependencies section
  3. Configure a property to define the output location for generated snippets
  4. Configure the test task to add the snippets directory as an output
  5. Configure the asciidoctor task
  6. Define an attribute named snippets that can be used when including the generated snippets in your documentation
  7. Make the task depend on the test task so that the tests are run before the documentation is created
  8. Configure the snippets directory as input. All the generated snippets will be created under this directory

Add the snippet directory as a property in pom.xml so the Asciidoctor plugin can use this path to generate the snippets under this folder:

 ${project.build.directory}/generated-snippets 

The Maven plugin configuration in the pom.xml to generate the Asciidoc snippets from the build is as below:

 org.asciidoctor asciidoctor-maven-plugin 1.5.6   generate-docs package  process-asciidoc   html book  ${snippetsDirectory}  src/docs/asciidocs target/generated-docs    

10. API Doc Generation Process

When the Maven build runs and the tests are executed, all the snippets will be generated in the snippets folder under the configured target/generated-snippets directory. Once the snippets are generated, the build process generates HTML output.

The generated HTML file is formatted and readable, so the REST documentation is ready to use. Every time the Maven build runs, the documents also get generated with the latest updates.

11. Conclusion

Having no documentation is better than wrong documentation, but Spring REST docs will help generate accurate documentation for RESTful services.

Като официален пролетен проект, той постига целите си, като използва три тестови библиотеки: Spring MVC Test, WebTestClient и REST Assured. Този метод за генериране на документация може да помогне за поддръжка на тестов подход за разработване и документиране на RESTful API.

Можете да намерите примерен проект въз основа на кода в тази статия в свързаното хранилище на GitHub.

ПОЧИВКА отдолу

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА