Spring REST Docs文档_字段

通过将手写文档与Spring MVC Test或WebTestClient自动生成的代码片段相结合,记录RESTful服务。

介绍

Spring REST Docs 的目的是帮助您为 RESTful 服务生成准确且可读的文档。

编写高质量的文档是困难的。 缓解这种困难的一种方法是使用非常适合工作的工具。 为此,Spring REST Docs 默认使用 Asciidoctor。 Asciidoctor处理纯文本并生成HTML,样式和布局以满足您的需求。 如果你愿意,你也可以将Spring REST Docs配置为使用Markdown。

Spring REST Docs 使用由使用 Spring MVC 的测试框架、Spring WebFlux 的 WebTestClient 或 REST Assured 5 编写的测试生成的片段。 这种测试驱动的方法有助于保证服务文档的准确性。 如果代码段不正确,则生成该代码段的测试将失败。

记录 RESTful 服务主要是描述其资源。 每个资源描述的两个关键部分是它使用的 HTTP 请求的详细信息和它生成的 HTTP 响应。 Spring REST Docs 允许您使用这些资源以及 HTTP 请求和响应,从而保护您的文档免受服务实现的内部细节的影响。 这种分离有助于记录服务的 API,而不是其实现。 它还使您可以自由地改进实现,而无需返工文档。

开始

本节介绍如何开始使用 Spring REST 文档。

示例应用程序

如果您想直接进入,可以使用许多示例应用程序。

要求

Spring REST Docs 具有以下最低要求:

  • 爪哇 17
  • 弹簧框架 6

此外,该模块需要 REST Assured 5.2。​​spring-restdocs-restassured​

构建配置

使用 Spring REST 文档的第一步是配置项目的构建。 Spring HATEOAS 和 Spring Data REST 示例分别包含一个 和,您可能希望将其用作参考。 以下清单描述了配置的关键部分:​​build.gradle​​​​pom.xml​

<dependency> 
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>{project-version}</version>
<scope>test</scope>
</dependency>

<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>{project-version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

在作用域中添加依赖项。 如果要使用 REST Assured 而不是 MockMvc,请分别添加依赖项 或。​​spring-restdocs-mockmvc​​​​test​​​​WebTestClient​​​​spring-restdocs-webtestclient​​​​spring-restdocs-restassured​

添加 Asciidoctor 插件。

使用允许将文档包含在包中​。​​prepare-package​

添加为 Asciidoctor 插件的依赖项。 这将自动将文件中使用的属性配置为指向 。 它还允许您使用块宏。​​spring-restdocs-asciidoctor​​​​snippets​​​​.adoc​​​​target/generated-snippets​​​​operation​

打包文档

您可能希望将生成的文档打包到项目的 jar 文件中 — 例如,让 Spring Boot 将其作为静态内容。 为此,请配置项目的生成,以便:

  1. 文档是在构建 jar 之前生成的
  2. 生成的文档包含在 jar 中

以下列表显示了如何在Maven和Gradle中执行此操作:

马文

格拉德尔

<plugin> 
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<!-- … -->
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.outputDirectory}/static/docs
</outputDirectory>
<resources>
<resource>
<directory>
${project.build.directory}/generated-docs
</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>

Asciidoctor插件的现有声明。

资源插件必须在 Asciidoctor 插件之后声明,因为它们绑定到同一阶段 (),并且资源插件必须在 Asciidoctor 插件之后运行,以确保在复制之前生成文档。​​prepare-package​

将生成的文档复制到构建输出的目录中,从该目录中将包含在 jar 文件中。​​static/docs​

生成文档片段

Spring REST Docs 使用 Spring MVC 的测试框架、Spring WebFlux 的 WebTestClient 或 REST Assured 向您正在记录的服务发出请求。 然后,它会为请求和生成的响应生成文档片段。

设置测试

设置测试的确切方式取决于您使用的测试框架。 Spring REST Docs 为 JUnit 5 和 JUnit 4 提供了一流的支持。 建议使用 JUnit 5。 其他框架,如TestNG,也受支持,尽管需要稍微多一点设置。

设置 JUnit 5 测试

使用 JUnit 5 时,生成文档片段的第一步是将 应用于测试类。 以下示例演示如何执行此操作:​​RestDocumentationExtension​

@ExtendWith(RestDocumentationExtension.class)
public class JUnit5ExampleTests {

在测试典型的 Spring 应用程序时,您还应该应用 :​​SpringExtension​

@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
public class JUnit5ExampleTests {

根据项目的构建工具自动配置输出目录:​​RestDocumentationExtension​

构建工具

输出目录

马文

​target/generated-snippets​

格拉德尔

​build/generated-snippets​

如果您使用的是 JUnit 5.1,则可以通过将扩展注册为测试类中的字段并在创建扩展时提供输出目录来覆盖默认值。 以下示例演示如何执行此操作:

public class JUnit5ExampleTests {

@RegisterExtension
final RestDocumentationExtension restDocumentation = new RestDocumentationExtension ("custom");

}

接下来,您必须提供一种方法来配置 MockMvc 或 WebTestClient,或 REST Assured。 以下清单显示了如何执行此操作:​​@BeforeEach​

private MockMvc mockMvc;

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

实例是使用 . 可以从 上的静态方法获取此类的实例。​​MockMvc​​​​MockMvcRestDocumentationConfigurer​​​​documentationConfiguration()​​​​org.springframework.restdocs.mockmvc.MockMvcRestDocumentation​

配置程序应用合理的默认值,并提供用于自定义配置的 API。 有关详细信息,请参阅配置部分。

设置 JUnit 4 测试

使用 JUnit 4 时,生成文档片段的第一步是声明一个注释为 JUnit 的字段。 以下示例演示如何执行此操作:​​public​​​​JUnitRestDocumentation​​​​@Rule​

@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

默认情况下,规则会自动配置基于项目的生成工具的输出目录:​​JUnitRestDocumentation​

构建工具

输出目录

马文

​target/generated-snippets​

格拉德尔

​build/generated-snippets​

您可以通过在创建实例时提供输出目录来覆盖默认值。 以下示例演示如何执行此操作:​​JUnitRestDocumentation​

@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("custom");

接下来,您必须提供一种方法来配置 MockMvc 或 WebTestClient,或 REST Assured。 以下示例演示如何执行此操作:​​@Before​

private MockMvc mockMvc;

@Autowired
private WebApplicationContext context;

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

实例是使用 . 可以从 上的静态方法获取此类的实例。​​MockMvc​​​​MockMvcRestDocumentationConfigurer​​​​documentationConfiguration()​​​​org.springframework.restdocs.mockmvc.MockMvcRestDocumentation​

配置程序应用合理的默认值,并提供用于自定义配置的 API。 有关详细信息,请参阅配置部分。

在没有 JUnit 的情况下设置测试

不使用 JUnit 时的配置与使用 JUnit 时的配置大致相似。 本节介绍主要区别。 TestNG示例还说明了该方法。

第一个区别是您应该使用 代替 。 此外,您不需要注释。 以下示例演示如何使用:​​ManualRestDocumentation​​​​JUnitRestDocumentation​​​​@Rule​​​​ManualRestDocumentation​

private ManualRestDocumentation restDocumentation = new ManualRestDocumentation();

其次,您必须在每次测试前致电。 您可以将其作为配置 MockMvc、WebTestClient 或 REST Assured 的方法的一部分。 以下示例演示如何执行此操作:​​ManualRestDocumentation.beforeTest(Class, String)​

private MockMvc mockMvc;

@Autowired
private WebApplicationContext context;

@BeforeMethod
public void setUp(Method method) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
this.restDocumentation.beforeTest(getClass(), method.getName());
}

最后,您必须在每次测试后致电。 以下示例显示了如何使用 TestNG 执行此操作:​​ManualRestDocumentation.afterTest​

@AfterMethod
public void tearDown() {
this.restDocumentation.afterTest();
}

调用 RESTful 服务

现在,您已经配置了测试框架,您可以使用它来调用 RESTful 服务并记录请求和响应。 以下示例演示如何执行此操作:

this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) 
.andExpect(status().isOk())
.andDo(document("index"));

调用服务的根 () 并指示需要响应。​​/​​​​application/json​

断言服务产生了预期的响应。

记录对服务的调用,将代码段写入名为 (位于配置的输出目录下方)的目录中。 这些代码段由 . 可以从 上的静态方法获取此类的实例。​​index​​​​RestDocumentationResultHandler​​​​document​​​​org.springframework.restdocs.mockmvc.MockMvcRestDocumentation​

默认情况下,将写入六个代码片段:

  • ​<output-directory>/index/curl-request.adoc​
  • ​<output-directory>/index/http-request.adoc​
  • ​<output-directory>/index/http-response.adoc​
  • ​<output-directory>/index/httpie-request.adoc​
  • ​<output-directory>/index/request-body.adoc​
  • ​<output-directory>/index/response-body.adoc​

有关Spring REST Docs可以生成的这些代码片段和其他代码片段的更多信息,请参阅记录您的API。

使用代码段

在使用生成的代码段之前,必须创建源文件。 您可以随心所欲地命名文件,只要它有后缀。 生成的 HTML 文件具有相同的名称,但带有后缀。 源文件和生成的 HTML 文件的默认位置取决于您使用的是 Maven 还是 Gradle:​​.adoc​​​​.adoc​​​​.html​

构建工具

源文件

生成的文件

马文

​src/main/asciidoc/*.adoc​

​target/generated-docs/*.html​

格拉德尔

​src/docs/asciidoc/*.adoc​

​build/asciidoc/html5/*.html​

然后,您可以使用包含宏将生成的代码段包含在手动创建的 Asciidoc 文件中(本节前面所述)。 可以使用在构建配置中配置的自动设置的属性来引用代码段输出目录。 以下示例演示如何执行此操作:​​snippets​​​​spring-restdocs-asciidoctor​

include::{snippets}/index/curl-request.adoc[]

记录您的 API

本节提供了有关使用 Spring REST 文档记录 API 的更多详细信息。

超媒体

Spring REST Docs 支持在基于超媒体的 API 中记录链接。 以下示例演示如何使用它:

this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("index", links(
linkWithRel("alpha").description("Link to the alpha resource"),
linkWithRel("bravo").description("Link to the bravo resource"))));

配置 Spring REST 文档以生成描述响应链接的代码段。 对 使用静态方法。​​links​​​​org.springframework.restdocs.hypermedia.HypermediaDocumentation​

期望一个链接是 . 对 使用静态方法。​​rel​​​​alpha​​​​linkWithRel​​​​org.springframework.restdocs.hypermedia.HypermediaDocumentation​

期望一个链接是 .​​rel​​​​bravo​

结果是一个名为的代码段,其中包含描述资源链接的表。​​links.adoc​

如果响应中的链接具有 ,则可以从其描述符中省略说明并使用 。 如果省略说明,并且链接没有 ,则会发生故障。​​title​​​​title​​​​title​

记录链接时,如果在响应中找到未记录的链接,则测试将失败。 同样,如果在响应中找不到记录的链接,并且该链接未标记为可选,则测试也会失败。

如果不想记录链接,可以将其标记为已忽略。 这样做可以防止它出现在生成的代码段中,同时避免上述故障。

您还可以在宽松模式下记录链接,其中任何未记录的链接不会导致测试失败。 为此,请使用 上的方法。 在记录只想关注链接子集的特定方案时,这可能很有用。​​relaxedLinks​​​​org.springframework.restdocs.hypermedia.HypermediaDocumentation​

超媒体链接格式

默认情况下可以理解两种链接格式:

  • Atom:链接应位于名为 的数组中。 当响应的内容类型与 兼容时,缺省情况下使用此选项。linksapplication/json
  • HAL:链接应位于名为 的地图中。 当响应的内容类型与 兼容时,缺省情况下使用此选项。_linksapplication/hal+json

如果使用 Atom 或 HAL 格式的链接,但内容类型不同,则可以向 提供内置实现之一。 以下示例演示如何执行此操作:​​LinkExtractor​​​​links​

.andDo(document("index", links(halLinks(), 
linkWithRel("alpha").description("Link to the alpha resource"),
linkWithRel("bravo").description("Link to the bravo resource"))));

指示链接采用 HAL 格式。 对 使用静态方法。​​halLinks​​​​org.springframework.restdocs.hypermedia.HypermediaDocumentation​

如果您的 API 以 Atom 或 HAL 以外的格式表示其链接,则可以提供自己的接口实现,以从响应中提取链接。​​LinkExtractor​

忽略常见链接

与其记录每个响应共有的链接(例如使用 HAL 时的链接),不如在概述部分中记录一次链接,然后在 API 文档的其余部分中忽略它们。 为此,您可以在对重用代码段的支持的基础上,将链接描述符添加到预配置为忽略某些链接的代码段。 以下示例演示如何执行此操作:​​self​​​​curies​

public static LinksSnippet links(LinkDescriptor... descriptors) {
return HypermediaDocumentation.links(linkWithRel("self").ignored().optional(), linkWithRel("curies").ignored())
.and(descriptors);
}

请求和响应有效负载

除了前面描述的特定于超媒体的支持外,还提供了对请求和响应有效负载的一般文档的支持。

默认情况下,Spring REST Docs 会自动生成请求正文和响应正文的代码片段。 这些代码段分别命名为 和。​​request-body.adoc​​​​response-body.adoc​

请求和响应字段

为了提供请求或响应有效负载的更详细文档,提供了对记录有效负载字段的支持。

请考虑以下有效负载:

{
"contact": {
"name": "Jane Doe",
"email": "jane.doe@example.com"
}
}

您可以记录上一个示例的字段,如下所示:

this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("index", responseFields(
fieldWithPath("contact.email").description("The user's email address"),
fieldWithPath("contact.name").description("The user's name"))));

配置 Spring REST 文档以生成描述响应有效负载中的字段的代码段。 要记录请求,可以使用 。 两者都是 上的静态方法。​​requestFields​​​​org.springframework.restdocs.payload.PayloadDocumentation​

期望一个带有路径的字段。 对 使用静态方法。​​contact.email​​​​fieldWithPath​​​​org.springframework.restdocs.payload.PayloadDocumentation​

期望一个带有路径的字段。​​contact.name​

结果是一个代码段,其中包含描述字段的表。 对于请求,此代码段名为 . 对于响应,此代码段名为 。​​request-fields.adoc​​​​response-fields.adoc​

记录字段时,如果在有效负载中找到未记录的字段,则测试将失败。 同样,如果在有效负载中找不到记录的字段,并且该字段未标记为可选,则测试也会失败。

如果您不想为所有字段提供详细的文档,则可以记录有效负载的整个子部分。 以下示例演示如何执行此操作:

this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("index", responseFields(
subsectionWithPath("contact").description("The user's contact details"))));

使用路径 记录小节。 现在也被视为已被记录在案。 对 使用静态方法。​​contact​​​​contact.email​​​​contact.name​​​​subsectionWithPath​​​​org.springframework.restdocs.payload.PayloadDocumentation​

​subsectionWithPath​​可用于提供有效负载特定部分的高级概述。 然后,您可以为小节生成单独的、更详细的文档。 请参阅记录请求或响应有效负载的子部分。

如果根本不想记录字段或小节,可以将其标记为已忽略。 这可以防止它出现在生成的代码段中,同时避免前面描述的故障。

您还可以在宽松模式下记录字段,其中任何未记录的字段不会导致测试失败。 为此,请使用 上的 和 方法。 在记录只想关注有效负载子集的特定方案时,这可能很有用。​​relaxedRequestFields​​​​relaxedResponseFields​​​​org.springframework.restdocs.payload.PayloadDocumentation​

默认情况下,Spring REST Docs 假定您正在记录的有效负载是 JSON。 如果要记录 XML 有效负载,则请求或响应的内容类型必须与 兼容。​​application/xml​

JSON 有效负载中的字段

本节介绍如何使用 JSON 有效负载中的字段。

JSON 字段路径

JSON 字段路径使用点表示法或括号表示法。 点表示法使用“.”分隔路径中的每个键(例如,)。 括号表示法将每个键括在方括号和单引号中(例如,)。 在任一情况下, 用于标识数组。 点表示法更简洁,但使用括号表示法可以在键名中使用 (例如,)。 可以在同一路径中使用两种不同的表示法(例如,)。​​a.b​​​​['a']['b']​​​​[]​​​​.​​​​['a.b']​​​​a['b']​

请考虑以下 JSON 有效负载:

{
"a":{
"b":[
{
"c":"one"
},
{
"c":"two"
},
{
"d":"three"
}
],
"e.dot" : "four"
}
}

在前面的 JSON 有效负载中,以下路径都存在:

路径

价值

​a​

包含以下内容的对象​​b​

​a.b​

包含三个对象的数组

​['a']['b']​

包含三个对象的数组

​a['b']​

包含三个对象的数组

​['a'].b​

包含三个对象的数组

​a.b[]​

包含三个对象的数组

​a.b[].c​

包含字符串和​​one​​​​two​

​a.b[].d​

字符串​​three​

​a['e.dot']​

字符串​​four​

​['a']['e.dot']​

字符串​​four​

您还可以记录在其根目录中使用数组的有效负载。 路径引用整个数组。 然后,您可以使用括号或点表示法来标识数组条目中的字段。 例如, 对应于在以下数组中找到的每个对象的字段:​​[]​​​​[].id​​​​id​

[
{
"id":1
},
{
"id":2
}
]

您可以用作通配符来匹配具有不同名称的字段。 例如,可用于记录以下 JSON 中每个用户的角色:​​*​​​​users.*.role​

{
"users":{
"ab12cd34":{
"role": "Administrator"
},
"12ab34cd":{
"role": "Guest"
}
}
}
JSON 字段类型

当一个字段被记录下来时,Spring REST Docs会尝试通过检查有效负载来确定其类型。 支持七种不同的类型:

类型

描述

​array​

每次出现的字段的值都是一个数组。

​boolean​

每次出现的字段的值都是布尔值 ( 或 )。​​true​​​​false​

​object​

字段的每个匹配项的值都是一个对象。

​number​

字段的每个匹配项的值都是一个数字。

​null​

每次出现的字段的值为 。​​null​

​string​

每次出现的字段的值都是一个字符串。

​varies​

该字段在有效负载中多次出现,具有各种不同的类型。

还可以通过使用 上的方法显式设置类型。 所提供方法的结果在文档中使用。 通常,使用 枚举的值之一。 以下示例演示如何执行此操作:​​type(Object)​​​​FieldDescriptor​​​​toString​​​​Object​​​​JsonFieldType​

.andDo(document("index", responseFields(fieldWithPath("contact.email").type(JsonFieldType.STRING) 
.description("The user's email address"))));

将字段的类型设置为 。​​String​

XML 有效负载

本节介绍如何使用 XML 有效负载。

XML 字段路径

XML 字段路径使用 XPath 进行描述。 用于下降到子节点。/

XML 字段类型

记录 XML 有效负载时,必须使用 上的方法为字段提供类型。 文档中使用所提供类型方法的结果。type(Object)FieldDescriptortoString

重用字段描述符

除了对重用代码段的一般支持外,请求和响应代码段还允许使用路径前缀配置其他描述符。 这允许创建请求或响应有效负载的重复部分的描述符一次,然后重复使用。

考虑一个返回书籍的终结点:

{
"title": "Pride and Prejudice",
"author": "Jane Austen"
}

的路径分别为 和 和 。​​title​​​​author​​​​title​​​​author​

现在考虑一个返回书籍数组的端点:

[{
"title": "Pride and Prejudice",
"author": "Jane Austen"
},
{
"title": "To Kill a Mockingbird",
"author": "Harper Lee"
}]

的路径分别为 和 和 。 单本书和书籍数组之间的唯一区别是字段的路径现在有一个前缀。​​title​​​​author​​​​[].title​​​​[].author​​​​[].​

您可以创建记录书籍的描述符,如下所示:

FieldDescriptor[] book = new FieldDescriptor[] { fieldWithPath("title").description("Title of the book"),
fieldWithPath("author").description("Author of the book") };

然后,您可以使用它们来记录单个书籍,如下所示:

this.mockMvc.perform(get("/books/1").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("book", responseFields(book)));

记录并使用现有描述符​​title​​​​author​

您还可以使用描述符来记录书籍数组,如下所示:

this.mockMvc.perform(get("/books").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("book", responseFields(fieldWithPath("[]").description("An array of books"))
.andWithPrefix("[].", book)));

记录数组。

文档并使用前缀为​​[].title​​​​[].author​​​​[].​

记录请求或响应有效负载的子部分

如果有效负载很大或结构复杂,则记录有效负载的各个部分可能很有用。 REST 文档允许您通过提取有效负载的子部分然后记录它来做到这一点。

记录请求或响应正文的子部分

请考虑以下 JSON 响应正文:

{
"weather": {
"wind": {
"speed": 15.3,
"direction": 287.0
},
"temperature": {
"high": 21.2,
"low": 14.8
}
}
}

您可以生成一个代码段来记录对象,如下所示:​​temperature​

this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("location", responseBody(beneathPath("weather.temperature"))));

生成包含响应正文子部分的代码段。 在 上使用静态和方法。 要为请求正文生成代码段,可以使用 代替 。​​responseBody​​​​beneathPath​​​​org.springframework.restdocs.payload.PayloadDocumentation​​​​requestBody​​​​responseBody​

结果是一个包含以下内容的代码段:

{
"temperature": {
"high": 21.2,
"low": 14.8
}
}

为了使代码段的名称与众不同,其中包含该小节的标识符。 默认情况下,此标识符为 。 例如,前面的代码生成一个名为 的代码段。 可以使用该方法自定义标识符,如下所示:​​beneath-${path}​​​​response-body-beneath-weather.temperature.adoc​​​​withSubsectionId(String)​

responseBody(beneathPath("weather.temperature").withSubsectionId("temp"));

结果是一个名为 的代码段。​​request-body-temp.adoc​

记录请求或响应的子部分的字段

除了记录请求或响应正文的子部分外,还可以记录特定小节中的字段。 您可以生成一个代码段来记录对象(和)的字段,如下所示:​​temperature​​​​high​​​​low​

this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("location", responseFields(beneathPath("weather.temperature"),
fieldWithPath("high").description("The forecast high in degrees celcius"),
fieldWithPath("low").description("The forecast low in degrees celcius"))));

生成一个代码段,描述路径下方响应有效负载子部分中的字段。 对 使用静态方法。​​weather.temperature​​​​beneathPath​​​​org.springframework.restdocs.payload.PayloadDocumentation​

记录 和 字段。​​high​​​​low​

结果是一个代码段,其中包含一个描述 和 字段的表。 为了使代码段的名称与众不同,其中包含该小节的标识符。 默认情况下,此标识符为 。 例如,前面的代码生成一个名为 的代码段。​​high​​​​low​​​​weather.temperature​​​​beneath-${path}​​​​response-fields-beneath-weather.temperature.adoc​

查询参数

您可以使用 记录请求的查询参数。 以下示例演示如何执行此操作:​​queryParameters​

this.mockMvc.perform(get("/users?page=2&per_page=100")) 
.andExpect(status().isOk()).andDo(document("users", queryParameters(
parameterWithName("page").description("The page to retrieve"),
parameterWithName("per_page").description("Entries per page")
)));

在查询字符串中使用两个参数和 执行请求。​​GET​​​​page​​​​per_page​

配置 Spring REST 文档以生成描述请求查询参数的代码段。 对 使用静态方法。​​queryParameters​​​​org.springframework.restdocs.request.RequestDocumentation​

记录参数。 对 使用静态方法。​​page​​​​parameterWithName​​​​org.springframework.restdocs.request.RequestDocumentation​

记录参数。​​per_page​

记录查询参数时,如果在请求的查询字符串中使用未记录的查询参数,则测试将失败。 同样,如果在请求的查询字符串中找不到记录的查询参数,并且该参数未标记为可选,则测试也会失败。

如果不想记录查询参数,可以将其标记为已忽略。 这可以防止它出现在生成的代码段中,同时避免上述故障。

您还可以在宽松模式下记录查询参数,其中任何未记录的参数不会导致测试失败。 为此,请使用 上的方法。 在记录只想关注查询参数子集的特定方案时,这可能很有用。​​relaxedQueryParameters​​​​org.springframework.restdocs.request.RequestDocumentation​

表单参数

您可以使用 记录请求的表单参数。 以下示例演示如何执行此操作:​​formParameters​

this.mockMvc.perform(post("/users").param("username", "Tester")) 
.andExpect(status().isCreated()).andDo(document("create-user", formParameters(
parameterWithName("username").description("The user's username")
)));

使用单个表单参数执行请求。​​POST​​​​username​

配置 Spring REST 文档以生成描述请求表单参数的代码段。 对 使用静态方法。​​formParameters​​​​org.springframework.restdocs.request.RequestDocumentation​

记录参数。 对 使用静态方法。​​username​​​​parameterWithName​​​​org.springframework.restdocs.request.RequestDocumentation​

在所有情况下,结果都是一个名为的代码段,其中包含一个描述资源支持的表单参数的表。​​form-parameters.adoc​

记录表单参数时,如果在请求正文中使用未记录的表单参数,则测试将失败。 同样,如果在请求正文中找不到记录的表单参数,并且表单参数未标记为可选,则测试也会失败。

如果不想记录表单参数,可以将其标记为已忽略。 这可以防止它出现在生成的代码段中,同时避免上述故障。

您还可以在宽松模式下记录表单参数,其中任何未记录的参数不会导致测试失败。 为此,请使用 上的方法。 这在记录您只想关注表单参数子集的特定场景时非常有用。​​relaxedFormParameters​​​​org.springframework.restdocs.request.RequestDocumentation​

路径参数

您可以使用 记录请求的路径参数。 以下示例演示如何执行此操作:​​pathParameters​

this.mockMvc.perform(get("/locations/{latitude}/{longitude}", 51.5072, 0.1275)) 
.andExpect(status().isOk()).andDo(document("locations", pathParameters(
parameterWithName("latitude").description("The location's latitude"),
parameterWithName("longitude").description("The location's longitude")
)));

使用两个路径参数执行请求,以及 。​​GET​​​​latitude​​​​longitude​

配置 Spring REST 文档以生成描述请求路径参数的代码段。 对 使用静态方法。​​pathParameters​​​​org.springframework.restdocs.request.RequestDocumentation​

记录名为 的参数。 对 使用静态方法。​​latitude​​​​parameterWithName​​​​org.springframework.restdocs.request.RequestDocumentation​

记录名为 的参数。​​longitude​

结果是一个名为的代码段,其中包含一个描述资源支持的路径参数的表。​​path-parameters.adoc​

如果使用 MockMvc,要使路径参数可用于文档,则必须使用 on 上的一种方法而不是 来构建请求。​​RestDocumentationRequestBuilders​​​​MockMvcRequestBuilders​

记录路径参数时,如果在请求中使用了未记录的路径参数,则测试将失败。 同样,如果在请求中找不到记录的路径参数,并且路径参数未标记为可选,则测试也会失败。

您还可以在宽松模式下记录路径参数,其中任何未记录的参数不会导致测试失败。 为此,请使用 上的方法。 在记录只想关注路径参数子集的特定方案时,这可能很有用。​​relaxedPathParameters​​​​org.springframework.restdocs.request.RequestDocumentation​

如果不想记录路径参数,可以将其标记为已忽略。 这样做可以防止它出现在生成的代码段中,同时避免前面描述的失败。

​索取零件​

您可以使用 来记录分段请求的各个部分。 以下示例演示如何执行此操作:​​requestParts​

this.mockMvc.perform(multipart("/upload").file("file", "example".getBytes())) 
.andExpect(status().isOk()).andDo(document("upload", requestParts(
partWithName("file").description("The file to upload"))
));

使用名为 的单个部件执行请求。​​POST​​​​file​

配置 Spring REST 文档以生成描述请求部分的代码片段。 对 使用静态方法。​​requestParts​​​​org.springframework.restdocs.request.RequestDocumentation​

记录名为 的部件。 对 使用静态方法。​​file​​​​partWithName​​​​org.springframework.restdocs.request.RequestDocumentation​

结果是一个名为的代码段,其中包含一个描述资源支持的请求部分的表。​​request-parts.adoc​

记录请求部分时,如果在请求中使用了未记录的部分,则测试将失败。 同样,如果在请求中找不到记录的部件,并且该部件未标记为可选,则测试也会失败。

您还可以在宽松模式下记录请求部件,其中任何未记录的部件不会导致测试失败。 为此,请使用 上的方法。 在记录只想关注请求部分子集的特定方案时,这可能很有用。​​relaxedRequestParts​​​​org.springframework.restdocs.request.RequestDocumentation​

如果不想记录请求部分,可以将其标记为已忽略。 这可以防止它出现在生成的代码段中,同时避免前面描述的故障。

请求零件有效负载

您可以以与记录请求有效负载大致相同的方式记录请求部分的有效负载,并支持记录请求部分的正文及其字段。

记录请求部件的正文

您可以生成包含请求部分正文的代码段,如下所示:

MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png", "<<png data>>".getBytes());
MockMultipartFile metadata = new MockMultipartFile("metadata", "", "application/json",
"{ \"version\": \"1.0\"}".getBytes());

this.mockMvc.perform(multipart("/images").file(image).file(metadata).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("image-upload", requestPartBody("metadata")));

配置 Spring REST 文档以生成一个代码段,其中包含名为 的请求部分的正文。 对 使用静态方法。​​metadata​​​​requestPartBody​​​​PayloadDocumentation​

结果是一个名为的代码段,其中包含部件的主体。 例如,记录名为的部件会生成一个名为 的代码段。​​request-part-${part-name}-body.adoc​​​​metadata​​​​request-part-metadata-body.adoc​

记录请求部分的字段

您可以以与记录请求或响应字段大致相同的方式记录请求部分的字段,如下所示:

MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png", "<<png data>>".getBytes());
MockMultipartFile metadata = new MockMultipartFile("metadata", "", "application/json",
"{ \"version\": \"1.0\"}".getBytes());

this.mockMvc.perform(multipart("/images").file(image).file(metadata).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("image-upload", requestPartFields("metadata",
fieldWithPath("version").description("The version of the image"))));

配置 Spring REST 文档以生成一个片段,描述名为 的请求部分的有效负载中的字段。 对 使用静态方法。​​metadata​​​​requestPartFields​​​​PayloadDocumentation​

期望一个带有路径的字段。 对 使用静态方法。​​version​​​​fieldWithPath​​​​org.springframework.restdocs.payload.PayloadDocumentation​

结果是一个代码段,其中包含描述部件字段的表。 此代码段名为 。 例如,记录名为的部件会生成一个名为 的代码段。​​request-part-${part-name}-fields.adoc​​​​metadata​​​​request-part-metadata-fields.adoc​

记录字段时,如果在部件的有效负载中找到未记录的字段,则测试将失败。 同样,如果在部件的有效负载中找不到记录的字段,并且该字段未标记为可选,则测试也会失败。 对于具有分层结构的有效负载,记录字段足以使其所有后代也被视为已记录。

如果不想记录字段,可以将其标记为已忽略。 这样做可以防止它出现在生成的代码段中,同时避免上述故障。

您还可以在宽松模式下记录字段,其中任何未记录的字段不会导致测试失败。 为此,请使用 上的方法。 在记录特定场景时,这非常有用,其中您只想关注部件有效负载的子集。​​relaxedRequestPartFields​​​​org.springframework.restdocs.payload.PayloadDocumentation​

有关描述字段、记录使用 XML 的有效负载等的更多信息,请参阅有关记录请求和响应有效负载的部分。

HTTP 标头

您可以分别使用 和 来记录请求或响应中的标头。 以下示例演示如何执行此操作:​​requestHeaders​​​​responseHeaders​

this.mockMvc.perform(get("/people").header("Authorization", "Basic dXNlcjpzZWNyZXQ=")) 
.andExpect(status().isOk()).andDo(document("headers", requestHeaders(
headerWithName("Authorization").description("Basic auth credentials")),
responseHeaders(
headerWithName("X-RateLimit-Limit")
.description("The total number of requests permitted per period"),
headerWithName("X-RateLimit-Remaining")
.description("Remaining requests permitted in current period"),
headerWithName("X-RateLimit-Reset")
.description("Time at which the rate limit period will reset"))));

使用使用基本身份验证的标头执行请求。​​GET​​​​Authorization​

配置 Spring REST 文档以生成描述请求标头的代码段。 对 使用静态方法。​​requestHeaders​​​​org.springframework.restdocs.headers.HeaderDocumentation​

记录标头。 对 使用静态方法。​​Authorization​​​​headerWithName​​​​org.springframework.restdocs.headers.HeaderDocumentation​

生成描述响应标头的代码段。 对 使用静态方法。​​responseHeaders​​​​org.springframework.restdocs.headers.HeaderDocumentation​

结果是一个名为的代码段和一个名为 的代码段。 每个都包含一个描述标头的表。​​request-headers.adoc​​​​response-headers.adoc​

记录 HTTP 标头时,如果在请求或响应中找不到记录的标头,则测试将失败。

HTTP 饼干

您可以分别使用 和 在请求或响应中记录 Cookie。 以下示例演示如何执行此操作:​​requestCookies​​​​responseCookies​

this.mockMvc.perform(get("/").cookie(new Cookie("JSESSIONID", "ACBCDFD0FF93D5BB"))) 
.andExpect(status().isOk()).andDo(document("cookies", requestCookies(
cookieWithName("JSESSIONID").description("Session token")),
responseCookies(
cookieWithName("JSESSIONID").description("Updated session token"),
cookieWithName("logged_in")
.description("Set to true if the user is currently logged in"))));

使用 cookie 发出 GET 请求。​​JSESSIONID​

配置 Spring REST 文档以生成描述请求 cookie 的代码片段。 对 使用静态方法。​​requestCookies​​​​org.springframework.restdocs.cookies.CookieDocumentation​

记录饼干。对 使用静态方法。​​JSESSIONID​​​​cookieWithName​​​​org.springframework.restdocs.cookies.CookieDocumentation​

生成描述响应 Cookie 的代码段。 对 使用静态方法。​​responseCookies​​​​org.springframework.restdocs.cookies.CookieDocumentation​

结果是一个名为的代码段和一个名为 的代码段。 每个都包含一个描述 Cookie 的表格。​​request-cookies.adoc​​​​response-cookies.adoc​

记录 HTTP cookie 时,如果在请求或响应中找到未记录的 cookie,则测试将失败。 同样,如果未找到记录的 Cookie 并且该 Cookie 未标记为可选,则测试也会失败。 您还可以在宽松模式下记录 Cookie,其中任何未记录的 Cookie 不会导致测试失败。 为此,请使用 上的 和 方法。 在记录您只想关注 Cookie 子集的特定场景时,这可能很有用。 如果您不想记录 Cookie,可以将其标记为已忽略。 这样做可以防止它出现在生成的代码段中,同时避免前面描述的失败。​​relaxedRequestCookies​​​​relaxedResponseCookies​​​​org.springframework.restdocs.cookies.CookieDocumentation​

重用代码段

正在记录的 API 通常具有一些在其多个资源中通用的功能。 为了避免在记录此类资源时重复,您可以重用配置的通用元素。​​Snippet​

首先,创建描述常见元素的。 以下示例演示如何执行此操作:​​Snippet​

protected final LinksSnippet pagingLinks = links(
linkWithRel("first").optional().description("The first page of results"),
linkWithRel("last").optional().description("The last page of results"),
linkWithRel("next").optional().description("The next page of results"),
linkWithRel("prev").optional().description("The previous page of results"));

其次,使用此代码段并添加更多特定于资源的描述符。 以下示例演示如何执行此操作:

this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andDo(document("example", this.pagingLinks.and(
linkWithRel("alpha").description("Link to the alpha resource"),
linkWithRel("bravo").description("Link to the bravo resource"))));

重用 ,调用以添加特定于正在记录的资源的描述符。​​pagingLinks​​​​Snippet​​​​and​

该示例的结果是,与 、 和 的值的链接都被记录下来。​​rel​​​​first​​​​last​​​​next​​​​previous​​​​alpha​​​​bravo​

记录约束

Spring REST Docs 提供了许多类,可以帮助您记录约束。 可以使用 的实例来访问类约束的说明。 以下示例演示如何执行此操作:​​ConstraintDescriptions​

public void example() {
ConstraintDescriptions userConstraints = new ConstraintDescriptions(UserInput.class);
List<String> descriptions = userConstraints.descriptionsForProperty("name");
}

static class UserInput {

@NotNull
@Size(min = 1)
String name;

@NotNull
@Size(min = 8)
String password;

}

为类创建 的实例。​​ConstraintDescriptions​​​​UserInput​

获取属性约束的说明。 此列表包含两个说明:一个用于约束,一个用于约束。​​name​​​​NotNull​​​​Size​

Spring HATEOAS 示例中的 ApiDocumentation 类演示了此功能的实际应用。

查找约束

默认情况下,通过使用 Bean 验证 来查找约束。 目前仅支持属性约束。 您可以通过使用自定义实例创建来自定义所使用的实例。 要完全控制约束解析,可以使用自己的实现。​​Validator​​​​Validator​​​​ConstraintDescriptions​​​​ValidatorConstraintResolver​​​​ConstraintResolver​

描述约束

为所有 Bean 验证 3.0 的约束提供了默认描述:

  • ​AssertFalse​
  • ​AssertTrue​
  • ​DecimalMax​
  • ​DecimalMin​
  • ​Digits​
  • ​Email​
  • ​Future​
  • ​FutureOrPresent​
  • ​Max​
  • ​Min​
  • ​Negative​
  • ​NegativeOrZero​
  • ​NotBlank​
  • ​NotEmpty​
  • ​NotNull​
  • ​Null​
  • ​Past​
  • ​PastOrPresent​
  • ​Pattern​
  • ​Positive​
  • ​PositiveOrZero​
  • ​Size​

还提供了来自 Hibernate 的以下约束的默认描述 验证人:

  • ​CodePointLength​
  • ​CreditCardNumber​
  • ​Currency​
  • ​EAN​
  • ​Email​
  • ​Length​
  • ​LuhnCheck​
  • ​Mod10Check​
  • ​Mod11Check​
  • ​NotBlank​
  • ​NotEmpty​
  • ​Currency​
  • ​Range​
  • ​SafeHtml​
  • ​URL​

要覆盖缺省描述或提供新描述,可以创建基本名称为 . 基于 HATEOAS 的 Spring 示例包含此类资源包的示例。​​org.springframework.restdocs.constraints.ConstraintDescriptions​

资源包中的每个键都是约束的完全限定名称加上 . 例如,标准约束的键是 。​​.description​​​​@NotNull​​​​jakarta.validation.constraints.NotNull.description​

可以在约束说明中使用引用约束属性的属性占位符。 例如,约束的默认说明 引用约束的属性。​​@Min​​​​Must be at least ${value}​​​​value​

若要更好地控制约束描述解析,可以使用自定义 . 要获得完全控制,您可以使用自定义实现进行创建。​​ConstraintDescriptions​​​​ResourceBundleConstraintDescriptionResolver​​​​ConstraintDescriptions​​​​ConstraintDescriptionResolver​

在生成的代码段中使用约束说明

获得约束的描述后,您可以在生成的代码段中随意使用它们。 例如,您可能希望将约束说明作为字段说明的一部分包括在内。 或者,您可以将约束作为额外信息包含在请求字段代码段中。 基于 HATEOAS 的 Spring 示例中的 ApiDocumentation 类说明了后一种方法。

默认代码段

当您记录请求和响应时,会自动生成许多代码段。

片段

描述

​curl-request.adoc​

包含与正在记录的调用等效的 curl​ 命令。​​MockMvc​

​httpie-request.adoc​

包含等效于正在记录的调用的 HTTPie​ 命令。​​MockMvc​

​http-request.adoc​

包含等效于正在记录的调用的 HTTP 请求。​​MockMvc​

​http-response.adoc​

包含返回的 HTTP 响应。

​request-body.adoc​

包含已发送的请求的正文。

​response-body.adoc​

包含返回的响应的正文。

您可以配置默认生成的代码段。 有关详细信息,请参阅配置部分。

使用参数化输出目录

您可以参数化 使用的输出目录。 支持以下参数:​​document​

参数

描述

{方法名称}

测试方法的未修改名称。

{方法名称}

测试方法的名称,使用烤肉串盒进行格式化。

{method_name}

测试方法的名称,使用 snake_case 设置格式。

{类名}

测试类的未修改简单名称。

{类名}

测试类的简单名称,使用烤肉串大小写进行格式化。

{class_name}

测试类的简单名称,使用 snake_case 设置格式。

{步骤}

在当前测试中对服务进行的调用计数。

例如,在测试类上命名的测试方法中,将代码段写入名为 的目录中。​​document("{class-name}/{method-name}")​​​​creatingANote​​​​GettingStartedDocumentation​​​​getting-started-documentation/creating-a-note​

参数化输出目录与方法结合使用特别有用。 它允许在设置方法中配置一次文档,然后在类中的每个测试中重复使用。 以下示例演示如何执行此操作:​​@Before​

@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).alwaysDo(document("{method-name}/{step}/"))
.build();
}

使用此配置后,对要测试的服务的每次调用都会生成默认代码段,而无需任何进一步的配置。 查看每个示例应用程序中的类,以了解此功能的实际应用。​​GettingStartedDocumentation​

自定义输出

本节介绍如何自定义 Spring REST 文档的输出。

自定义生成的代码段

Spring REST Docs 使用 Mustache 模板来生成生成的片段。为Spring REST Docs可以生成的每个代码段提供了默认模板。 要自定义代码段的内容,您可以提供自己的模板。

模板从子包的类路径加载。 子包的名称由正在使用的模板格式的 ID 确定。 默认模板格式 Asciidoctor的 ID 为 ,因此代码段是从 加载的。 每个模板都以其生成的代码段命名。 例如,要覆盖代码段的模板,请创建一个名为 的模板。​​org.springframework.restdocs.templates​​​​asciidoctor​​​​org.springframework.restdocs.templates.asciidoctor​​​​curl-request.adoc​​​​curl-request.snippet​​​​src/test/resources/org/springframework/restdocs/templates/asciidoctor​

包括额外信息

有两种方法可以提供额外的信息以包含在生成的代码段中:

  • 对描述符使用该方法向其添加一个或多个属性。attributes
  • 调用 、、 等时传入一些属性。 此类属性与整个代码段相关联。curlRequesthttpRequesthttpResponse

任何其他属性在模板呈现过程中都可用。 结合自定义代码段模板,可以在生成的代码段中包含额外的信息。

一个具体的例子是在记录请求字段时添加约束列和标题。 第一步是为您记录的每个字段提供一个属性,并提供一个属性。 以下示例演示如何执行此操作:​​constraints​​​​title​

.andDo(document("create-user", requestFields(attributes(key("title").value("Fields for user creation")), 
fieldWithPath("name").description("The user's name")
.attributes(key("constraints").value("Must not be null. Must not be empty")),
fieldWithPath("email").description("The user's email address")
.attributes(key("constraints").value("Must be a valid email address")))));

配置请求字段代码段的属性。​​title​

设置字段的属性。​​constraints​​​​name​

设置字段的属性。​​constraints​​​​email​

第二步是提供一个名为的自定义模板,该模板包含有关生成的代码段表中字段约束的信息,并添加标题。​​request-fields.snippet​

.{{title}} 
|===
|Path|Type|Description|Constraints

{{#fields}}
|{{path}}
|{{type}}
|{{description}}
|{{constraints}}

{{/fields}}
|===

向表格添加标题。

添加一个名为“约束”的新列。

在表的每一行中包含描述符的属性。​​constraints​