Spring REST Docs简介

Introduction to Spring REST Docs

1.概述

Spring REST Docs为准确的RESTful服务生成文档。 它结合了手写文档和由Spring测试生成的自动生成的文档片段。

2.优势

该项目背后的主要哲学是使用测试来生成文档。 这样可以确保始终生成的文档与API的实际行为准确匹配。 此外,输出准备好由Asciidoctor处理,Ascidoctor是一个以AsciiDoc语法为中心的发布工具链。 这是用于生成Spring Framework文档的工具。

这些方法减少了其他框架带来的限制。 Spring REST Docs生成的文档准确,简洁且结构合理。 然后,该文档使Web服务使用者可以毫不费力地获取所需的信息。

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

  • 生成curl和http请求片段

  • 易于将文档打包到项目jar文件中

  • 轻松向片段添加额外信息

  • 同时支持JSON和XML

  • 可以使用Spring MVC测试支持,Spring Webflux的WebTestClient或REST-Assured编写生成代码片段的测试。

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

    3.依存关系

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

    1
    2
    3
    4
    5
    <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 Test框架向要记录的REST服务发出请求。 运行测试将为请求和结果响应生成文档摘要。

    我们可以将库与JUnit 4和JUnit 5测试一起使用。 让我们看看每个配置所必需的。

    4.1。 JUnit 4配置

    生成用于JUnit 4测试的文档摘要的第一步是声明一个公共JUnitRestDocumentation字段,该字段被注释为JUnit @Rule。

    JUnitRestDocumentation规则配置了输出目录,应将生成的代码段保存到该目录中。 例如,此目录可以是Maven的构建目录:

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

    接下来,我们设置MockMvc上下文,以便将其配置为生成文档:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

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

    使用MockMvcRestDocumentationConfigurer配置MockMvc对象。 此类的实例可以从org.springframework.restdocs.mockmvc.MockMvcRestDocumentation上的static documentationConfiguration()方法获得。

    4.2。 JUnit 5配置

    要使用JUnit 5测试,我们必须使用RestDocumentationExtension类扩展测试:

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

    使用Maven或/ build / generate-snippets用于Gradle时,此类会自动使用/ target / generate-snippets输出目录进行配置。

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

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

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

    5. RESTful服务

    让我们创建一个可以记录的CRUD RESTful服务:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @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基本端点的链接:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @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测试

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

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

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

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

    让我们继续定制一些电话。

    要记录包含链接的索引页,我们可以使用static links()方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @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()记录请求有效负载:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @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"),
            ))));
    }

    在此示例中,我们记录了POST请求,该请求接收带有title和body字段的CrudInput模型并发送CREATED状态。 使用fieldWithPath()方法记录每个字段。

    要记录请求和路径参数,我们可以使用requestParameters()和pathParameters()方法。 两种方法都使用parameterWithName()方法来描述每个参数:

    1
    2
    3
    4
    5
    6
    7
    8
    @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 / generation-snippets文件夹中:

    Screen Shot 2016-04-04 at 11.48.52 PM

    生成的输出将包含有关服务,如何调用REST服务(如" curl"调用),来自REST服务的HTTP请求和响应以及服务的链接/端点的信息:

    CURL命令

    1
    2
    3
    ----
    $ curl 'http://localhost:8080/' -i
    ----

    HTTP – REST响应

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [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包含引用它们。 在我们的例子中,我们在src / docs中创建了一个名为api-guide.adoc的文档:

    Screen Shot 2016-05-01 at 8.51.48 PM

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

    1
    2
    3
    ==== Links

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

    9. Asciidocs Maven插件

    要将API指南从Asciidoc转换为可读格式,我们可以向构建生命周期添加Maven插件。 有几个步骤可以实现此目的:

  • 将Asciidoctor插件应用于pom.xml

  • 如依赖性部分所述,在testCompile配置中添加对spring-restdocs-mockmvc的依赖性

  • 配置属性以定义生成的代码片段的输出位置

  • 配置测试任务以将摘要目录添加为输出

  • 配置asciidoctor任务

  • 定义一个名为片段的属性,当在文档中包含生成的片段时可以使用该属性

  • 使任务取决于测试任务,以便在创建文档之前运行测试

  • 将摘要目录配置为输入。 所有生成的代码段都将在此目录下创建

  • 将片段目录添加为pom.xml中的属性,以便Asciidoctor插件可以使用此路径在此文件夹下生成片段:

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

    pom.xml中的Maven插件配置如下所示以从构建生成Asciidoc代码片段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <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输出。

    Screen Shot 2016-05-08 at 11.32.25 PM

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

     width=

    11.结论

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

    作为一个正式的Spring项目,它通过使用三个测试库来实现其目标:Spring MVC Test,WebTestClient和REST Assured。 这种生成文档的方法可以帮助支持测试驱动的方法来开发和文档RESTful API。

    您可以在链接的GitHub存储库中基于本文中的代码找到示例项目。