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:
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
Вы также можете проверить Maven Central на наличие новой версии зависимости здесь .
В нашем примере нам нужна зависимость spring-restdocs-mockmvc,
так как мы используем поддержку тестов Spring MVC для создания наших тестов.
Если мы хотим писать тесты с использованием WebTestClient или REST Assured, нам понадобятся зависимости spring-restdocs-webtestclient и spring-restdocs-restassured .
4. Конфигурация
Как уже упоминалось, мы будем использовать тестовую среду Spring MVC для выполнения запросов к службам 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
. Экземпляр этого класса можно получить из статического метода documentConfiguration() в
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. REST-сервис
Давайте создадим службу CRUD RESTful, которую мы можем задокументировать:
@RestController
@RequestMapping("/crud")
public class CRUDController {
@GetMapping
public List<CrudInput> read(@RequestBody CrudInput crudInput) {
List<CrudInput> returnList = new ArrayList<CrudInput>();
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<CustomRepresentationModel> {
public CustomRepresentationModel(Link initialLink) {
super(initialLink);
}
}
@GetMapping
public CustomRepresentationModel index() {
return new CustomRepresentationModel(linkTo(CRUDController.class).withRel("crud"));
}
}
6. Юнит-тесты
Вернувшись к тестам, мы можем использовать экземпляр MockMvc
для вызова наших сервисов и документирования запроса и ответа.
Во- первых, чтобы каждый вызов MockMvc
автоматически документировался без какой-либо дополнительной настройки, мы можем использовать метод alwaysDo ()
:
this.mockMvc = MockMvcBuilders
//...
.alwaysDo(document("{method-name}",
preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
.build();
Эта настройка гарантирует, что для каждого вызова MockMvc
фрагменты по умолчанию создаются в папке с именем тестового метода. Кроме того, применение препроцессора prettyPrint() позволяет
отображать фрагменты в более удобочитаемом виде.
Давайте продолжим настройку некоторых наших вызовов.
Чтобы задокументировать нашу индексную страницу, содержащую ссылку, мы можем использовать метод static links()
:
@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"))));
}
Здесь мы используем метод linkWithRel()
для документирования ссылки на /crud.
Чтобы добавить заголовок Content-Type
к ответу, мы документируем его с помощью метода headerWithName()
и добавляем в метод responseHeaders()
.
Мы также документируем полезную нагрузку ответа, используя метод responseFields()
. Это можно использовать для документирования более сложного подраздела ответа или отдельного поля с помощью методов subsectionWithPath() или fieldWithPath().
Подобно полезной нагрузке ответа, мы также можем документировать полезную нагрузку запроса, используя requestPayload():
@Test
public void crudCreateExample() throws Exception {
Map<String, Object> crud = new HashMap<>();
crud.put("title", "Sample Model");
crud.put("body", "http://www.foreach.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"),
))));
}
В этом примере мы задокументировали наш POST-запрос, который получает модель CrudInput
с полями title и body и отправляет статус CREATED. Каждое поле документируется с помощью метода fieldWithPath()
.
Чтобы документировать параметры запроса и пути, мы можем использовать методы requestParameters()
и pathParameters()
. Оба метода используют метод parameterWithName()
для описания каждого параметра:
@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")
)));
}
Здесь мы задокументировали нашу конечную точку удаления, которая получает параметр пути id .
Проект Spring REST Docs содержит еще более мощные функции документации, такие как ограничения полей и части запросов, которые можно найти в документации .
7. Выход
После успешного запуска сборки выходные фрагменты документов REST будут сгенерированы и сохранены в папке target/generated-snippets
:
Сгенерированный вывод будет содержать информацию о сервисе, о том, как вызывать сервис REST, например вызовы curl, HTTP-запрос и ответ от сервиса REST, а также ссылки/конечные точки на сервис:
<strong>Команда CURL</strong>
----
$ curl 'http://localhost:8080/' -i
----
<strong>HTTP — ответ REST</strong>
[source,http,options="nowrap"]
----
HTTP/1.1 200 OK
Content-Type: application/hal+json;charset=UTF-8
Content-Length: 93
{
"_links" : {
"crud" : {
"href" : "http://localhost:8080/crud"
}
}
}
----
8. Использование сниппетов для создания документации
Чтобы использовать фрагменты в большом документе, вы можете ссылаться на них, используя Asciidoc include .
В нашем случае мы создали документ в src/docs
с именем api-guide.adoc
:
В этом документе, если мы хотим сослаться на фрагмент ссылки, мы можем включить его, используя заполнитель {snippets}
, который будет заменен Maven при обработке документа:
==== Links
include::{snippets}/index-example/links.adoc[]
9. Плагины Asciidocs Maven
Чтобы преобразовать руководство по API из Asciidoc в удобочитаемый формат, мы можем добавить плагин Maven в жизненный цикл сборки. Есть несколько шагов, чтобы включить это:
- Примените плагин Asciidoctor к
файлу pom.xml.
- Добавьте зависимость от
spring-restdocs-mockmvc
в конфигурациюtestCompile
, как указано в разделе зависимостей. - Настройте свойство, чтобы определить выходное местоположение для сгенерированных фрагментов.
- Настройте
тестовую
задачу, чтобы добавить каталог фрагментов в качестве выходных данных. - Настроить задачу
asciidoctor
- Определите атрибут с именем
snippets
, который можно использовать при включении сгенерированных фрагментов в вашу документацию. - Сделайте задачу зависимой от
тестовой
задачи, чтобы тесты запускались до создания документации - Настройте каталог
фрагментов в качестве входных данных.
Все сгенерированные фрагменты будут созданы в этом каталоге.
Добавьте каталог фрагментов в качестве свойства в pom.xml
, чтобы подключаемый модуль Asciidoctor мог использовать этот путь для создания фрагментов в этой папке:
<properties>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
</properties>
Конфигурация плагина Maven в pom.xml
для создания фрагментов Asciidoc из сборки выглядит следующим образом:
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.6</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<attributes>
<snippets>${snippetsDirectory}</snippets>
</attributes>
<sourceDirectory>src/docs/asciidocs</sourceDirectory>
<outputDirectory>target/generated-docs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
10. Процесс создания документации API
При запуске сборки Maven и выполнении тестов все фрагменты будут созданы в папке фрагментов в настроенном каталоге target/generated-snippets
. После создания фрагментов процесс сборки генерирует выходные данные HTML.
Сгенерированный файл HTML отформатирован и доступен для чтения, поэтому документация REST готова к использованию. Каждый раз, когда запускается сборка Maven, документы также генерируются с последними обновлениями.
11. Заключение
Отсутствие документации лучше, чем неправильная документация, но документы Spring REST помогут создать точную документацию для служб RESTful.
Как официальный проект Spring, он достигает своих целей с помощью трех тестовых библиотек: Spring MVC Test, WebTestClient
и REST Assured. Этот метод создания документации может помочь в поддержке основанного на тестировании подхода к разработке и документированию RESTful API.
Вы можете найти пример проекта на основе кода из этой статьи в связанном репозитории GitHub .