Introduction to Spring REST Docs – Spring REST文档介绍

最后修改: 2016年 5月 23日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Spring REST Docs generates documentation for RESTful services that is both accurate and readable. It combines hand-written documentation with auto-generated document snippets produced with Spring tests.

Spring REST Docs可以为RESTful服务生成既准确又可读的文档。它将手写的文档与Spring测试产生的自动生成的文档片段相结合。

2. Advantages

2.优势

One major philosophy behind the project is the use of tests to produce the documentation. This ensures that the documentation always generated accurately matches the actual behavior of the API. Additionally, the output is ready to be processed by Asciidoctor, a publishing toolchain centered around the AsciiDoc syntax. This is the same tool that is used to generate the Spring Framework’s documentation.

该项目背后的一个主要理念是使用测试来生成文档。这确保了所生成的文档总是与API的实际行为准确匹配。此外,输出结果可由Asciidoctor处理,这是一个以AsciiDoc语法为核心的发布工具链。这也是用于生成Spring框架文档的同一工具。

These approaches reduce the limitations imposed by other frameworks. Spring REST Docs produces documentation that is accurate, concise, and well-structured. This documentation then allows the web service consumers to get the information they need with a minimum of fuss.

这些方法减少了其他框架所带来的限制。Spring REST Docs产生的文档是准确、简明和结构良好的。然后,该文档允许Web服务消费者以最少的麻烦获得他们需要的信息。

The tool has some other advantages, such as:

该工具还有一些其他的优点,如:。

  • curl and http request snippets are generated
  • easy to package documentation in projects jar file
  • easy to add extra information to the snippets
  • supports both JSON and XML

The tests that produce the snippets can be written using either Spring MVC Test support, Spring Webflux’s WebTestClient or REST-Assured.

产生片段的测试可以使用Spring MVC测试支持、Spring Webflux的WebTestClient或REST-Assured编写。

In our examples, we’re going to use Spring MVC tests, but using the other frameworks is very similar.

在我们的例子中,我们将使用Spring MVC测试,但使用其他框架也非常相似。

3. Dependencies

3.依赖性

The ideal way to get started using Spring REST Docs in a project is by using a dependency management system. Here, we’re using Maven as build tool, so the dependency below can be copied and pasted into your POM:

在项目中开始使用Spring REST Docs的理想方法是使用依赖管理系统。这里,我们使用Maven作为构建工具,所以下面的依赖关系可以复制并粘贴到你的POM中。

<dependency>
    <groupId>org.springframework.restdocs</groupId>
    <artifactId>spring-restdocs-mockmvc</artifactId>
    <version>2.0.4.RELEASE</version>
</dependency>

You can also check Maven Central for a new version of the dependency here.

您也可以查看Maven中心是否有新版本的依赖这里

In our example, we need the spring-restdocs-mockmvc dependency since we’re using the Spring MVC test support to create our tests.

在我们的例子中,我们需要spring-restdocs-mockmvc依赖关系,因为我们要使用Spring MVC测试支持来创建我们的测试。

If we want to write tests using WebTestClient or REST Assured, we’ll need the spring-restdocs-webtestclient and spring-restdocs-restassured dependencies.

如果我们想使用WebTestClient或REST Assured编写测试,我们需要spring-restdocs-webtestclientspring-restdocs-restassured依赖项。

4. Configuration

4.配置

As mentioned, we’ll use Spring MVC Test framework to make requests to the REST services which are to be documented. Running the test produces documentation snippets for the request and the resulting response.

如前所述,我们将使用Spring MVC测试框架对要记录的REST服务进行请求。运行测试会产生请求和响应的文档片段。

We can use the library with both JUnit 4 and JUnit 5 tests. Let’s see the configuration necessary for each.

我们可以在JUnit 4和JUnit 5测试中使用该库。让我们看看每种情况下的必要配置。

4.1. JUnit 4 Configuration

4.1.JUnit 4配置

The very first step in generating documentation snippets for JUnit 4 tests is to declare a public JUnitRestDocumentation field that is annotated as a JUnit @Rule.

为JUnit 4测试生成文档片段的第一步是声明一个公共的JUnitRestDocumentation字段,它被注释为JUnit@Rule

The JUnitRestDocumentation rule is configured with the output directory into which the generated snippets should be saved. For example, this directory can be the build out directory of Maven:

JUnitRestDocumentation规则配置了输出目录,生成的片段应保存在该目录中。例如,该目录可以是Maven的构建输出目录。

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

Next, we set up the MockMvc context so that it will be configured to produce documentation:

接下来,我们设置MockMvc上下文,这样它就会被配置为产生文档。

@Autowired
private WebApplicationContext context;

private MockMvc mockMvc;

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

The MockMvc object is configured using a MockMvcRestDocumentationConfigurer. An instance of this class can be obtained from the static documentationConfiguration() method on org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.

MockMvc对象是通过MockMvcRestDocumentationConfigurer来配置的。这个类的实例可以从org.springframework.restdocs.mockmvc.MockMvcRestDocumentation上的静态documentationConfiguration()方法获得。

4.2. JUnit 5 Configuration

4.2.JUnit 5的配置

To work with a JUnit 5 test, we have to extend the test with the RestDocumentationExtension class:

为了在JUnit 5测试中工作,我们必须用RestDocumentationExtension类来扩展测试:

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

This class is automatically configured with a /target/generated-snippets output directory when using Maven, or /build/generate-snippets for Gradle.

使用Maven时,该类会自动配置/target/generated-snippets输出目录,使用Gradle时,则配置/build/generate-snippets

Next, we have to set up the MockMvc instance in a @BeforeEach method:

接下来,我们必须在@BeforeEach方法中设置MockMvc实例。

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

If we’re not using JUnit for the tests, then we have to use the ManualRestDocumentation class.

如果我们不使用JUnit进行测试,那么我们必须使用ManualRestDocumentation类。

5. RESTful Service

5.RESTful服务</strong

Let’s create a CRUD RESTful service that we can document:

让我们创建一个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
    }
}

Then, let’s also add an IndexController that returns a page with a link to the CRUDController base endpoint:

然后,让我们也添加一个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. JUnit Tests

6.JUnit测试

Back in the tests, we can use the MockMvc instance to call our services and document the request and response.

回到测试中,我们可以使用MockMvc实例来调用我们的服务并记录请求和响应。

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

首先,为了确保每个MockMvc调用都被自动记录,而不需要进一步配置,我们可以使用alwaysDo()方法

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.

这样的设置可以确保每一次MockMvc的调用,默认的代码段都被创建在一个带有测试方法名称的文件夹中。此外,应用prettyPrint()预处理器可以以一种更容易阅读的方式显示片段。

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:

为了记录我们包含链接的索引页,我们可以使用静态的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"))));
}

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

在这里,我们使用linkWithRel()方法来记录一个指向/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.

为了给响应添加Content-Type头,我们使用headerWithName()方法记录它,并将它添加到responseHeaders()方法。

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.

我们还使用responseFields()方法来记录响应的有效载荷。这可用于记录响应的一个更复杂的小节,或使用subsectionWithPath()或fieldWithPath()方法记录一个单一字段。

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

与响应有效载荷类似,我们也可以使用requestPayload()来记录请求有效载荷:

@Test
public void crudCreateExample() throws Exception {
    Map<String, Object> crud = new HashMap<>();
    crud.put("title", "Sample Model");
    crud.put("body", "http://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.

在这个例子中,我们记录了我们的POST请求,它接收了一个带有标题和正文字段的CrudInput模型,并发送了一个CREATED状态。每个字段都使用fieldWithPath()方法进行记录。

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

为了记录请求和路径参数,我们可以使用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")
      )));
}

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

这里,我们记录了我们的删除端点,它接收一个id路径参数。

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.

Spring REST文档项目包含更强大的文档功能,如字段约束和请求部分,可以在文档中找到。

7. Output

7.输出

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:

一旦构建运行成功,将生成REST文档片段的输出,并保存到target/generated-snippets文件夹。

Screen Shot 2016-04-04 at 11.48.52 PM

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:

生成的输出将有关于服务的信息,如何调用REST服务,如’curl’调用,REST服务的HTTP请求和响应,以及服务的链接/端点。

<strong>CURL Command</strong>

CURL命令

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

<strong>HTTP – REST Response</strong>

HTTP – REST响应

[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. Using Snippets to Create Documentation

8.使用片段来创建文档

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:

要在更大的文件中使用这些片段,你可以使用Asciidoc的includes来引用它们。在我们的例子中,我们在src/docs中创建了一个名为api-guide.adoc的文件。

Screen Shot 2016-05-01 at 8.51.48 PM

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:

在该文档中,如果我们希望引用链接片段,我们可以使用占位符{snippets}包含它。,Maven会在处理文档时将其替换。

==== Links

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

9. Asciidocs Maven Plugins

9.Asciidocs的Maven插件

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:

为了将API指南从Asciidoc转换为可读格式,我们可以在构建生命周期中添加一个Maven插件。要实现这一点,有几个步骤。

  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:

pom.xml中添加片段目录作为属性,这样Asciidoctor插件就可以使用这个路径来生成这个文件夹下的片段。

<properties>
    <snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
</properties>

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

pom.xml中的Maven插件配置如下,以从构建中生成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 Doc Generation Process

10.API文件生成过程

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.

当Maven构建运行并执行测试时,所有片段将在配置的target/generated-snippets目录下的snippets文件夹中生成。一旦片段生成,构建过程就会生成HTML输出。

Screen Shot 2016-05-08 at 11.32.25 PM

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.

生成的HTML文件具有格式化和可读性,因此REST文档可以随时使用。每次Maven构建运行时,文档也会被生成最新的更新。

docs

11. Conclusion

11.结论

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

没有文档总比错误的文档好,但Spring REST文档将有助于为RESTful服务生成准确的文档。

As an official Spring project, it accomplishes its goals by using three test libraries: Spring MVC Test, WebTestClient and REST Assured. This method of generating documentation can help support a test-driven approach to developing and documenting RESTful APIs.

作为一个官方的Spring项目,它通过使用三个测试库来完成其目标。Spring MVC Test、WebTestClient和REST Assured。这种生成文档的方法可以帮助支持测试驱动的方法来开发和记录RESTful APIs。

You can find an example project based on the code in this article in the linked GitHub repository.

你可以在链接的GitHub资源库中找到一个基于本文代码的示例项目。