API文档化

本节提供了有关使用 Spring REST Docs 来记录 API 的更多详细信息。spring-doc.cadn.net.cn

超媒体

Spring REST Docs 支持在基于超媒体的 API 中记录链接。 以下示例显示了如何使用它:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("index", links((1)
			linkWithRel("alpha").description("Link to the alpha resource"), (2)
			linkWithRel("bravo").description("Link to the bravo resource")))); (3)
1 配置 Spring REST 文档以生成描述响应链接的代码段。 使用静态linksmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
2 预期relalpha. 使用静态linkWithRelmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
3 预期relbravo.
WebTest客户端
this.webTestClient.get()
	.uri("/")
	.accept(MediaType.APPLICATION_JSON)
	.exchange()
	.expectStatus()
	.isOk()
	.expectBody()
	.consumeWith(document("index", links((1)
			linkWithRel("alpha").description("Link to the alpha resource"), (2)
			linkWithRel("bravo").description("Link to the bravo resource")))); (3)
1 配置 Spring REST 文档以生成描述响应链接的代码段。 使用静态linksmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
2 预期relalpha. 使用静态linkWithRelmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
3 预期relbravo.
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("index", links((1)
			linkWithRel("alpha").description("Link to the alpha resource"), (2)
			linkWithRel("bravo").description("Link to the bravo resource")))) (3)
	.get("/")
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST 文档以生成描述响应链接的代码段。 使用静态linksmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
2 预期relalpha.使用静态linkWithRelmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
3 预期relbravo.

结果是一个名为links.adoc,其中包含一个描述资源链接的表。spring-doc.cadn.net.cn

如果响应中的链接具有title中,您可以从其描述符中省略描述,并且title被使用。 如果您省略了描述,并且链接没有title,则失败。

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

如果您不想记录链接,可以将其标记为已忽略。 这样做可以防止它出现在生成的代码段中,同时避免上述失败。spring-doc.cadn.net.cn

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

默认情况下,可以理解两种链接格式:spring-doc.cadn.net.cn

如果您使用 Atom 或 HAL 格式的链接,但具有不同的内容类型,则可以提供一个内置的LinkExtractorimplementations 设置为links. 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
.andDo(document("index", links(halLinks(), (1)
		linkWithRel("alpha").description("Link to the alpha resource"),
		linkWithRel("bravo").description("Link to the bravo resource"))));
1 指示链接采用 HAL 格式。 使用静态halLinksmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
WebTest客户端
.consumeWith(document("index", links(halLinks(), (1)
		linkWithRel("alpha").description("Link to the alpha resource"),
		linkWithRel("bravo").description("Link to the bravo resource"))));
1 指示链接采用 HAL 格式。 使用静态halLinksmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.
放心
.filter(document("index", links(halLinks(), (1)
		linkWithRel("alpha").description("Link to the alpha resource"),
		linkWithRel("bravo").description("Link to the bravo resource"))))
1 指示链接采用 HAL 格式。使用静态halLinksmethod 开启org.springframework.restdocs.hypermedia.HypermediaDocumentation.

如果您的 API 以 Atom 或 HAL 以外的格式表示其链接,则可以提供自己的LinkExtractor接口从响应中提取链接。spring-doc.cadn.net.cn

而不是记录每个响应通用的链接,例如selfcuries使用 HAL 时,您可能希望在概述部分记录一次它们,然后在 API 文档的其余部分忽略它们。 为此,您可以基于对重用代码段的支持,将链接描述符添加到预配置为忽略某些链接的代码段中。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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

请求和响应负载

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

默认情况下, Spring REST Docs 会自动为请求正文和响应正文生成片段。 这些代码段被命名为request-body.adocresponse-body.adoc分别。spring-doc.cadn.net.cn

请求和响应字段

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

请考虑以下有效负载:spring-doc.cadn.net.cn

{
	"contact": {
		"name": "Jane Doe",
		"email": "[email protected]"
	}
}

您可以按如下方式记录上一个示例的字段:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("index", responseFields((1)
			fieldWithPath("contact.email").description("The user's email address"), (2)
			fieldWithPath("contact.name").description("The user's name")))); (3)
1 配置 Spring REST 文档以生成描述响应有效负载中字段的代码段。 要记录请求,您可以使用requestFields. 两者都是org.springframework.restdocs.payload.PayloadDocumentation.
2 期望一个路径为contact.email. 使用静态fieldWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
3 期望一个路径为contact.name.
WebTest客户端
this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("user",
		responseFields((1)
			fieldWithPath("contact.email").description("The user's email address"), (2)
			fieldWithPath("contact.name").description("The user's name")))); (3)
1 配置 Spring REST 文档以生成描述响应有效负载中字段的代码段。 要记录请求,您可以使用requestFields. 两者都是org.springframework.restdocs.payload.PayloadDocumentation.
2 期望一个路径为contact.email. 使用静态fieldWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
3 期望一个路径为contact.name.
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("user", responseFields((1)
			fieldWithPath("contact.name").description("The user's name"), (2)
			fieldWithPath("contact.email").description("The user's email address")))) (3)
	.when()
	.get("/user/5")
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST 文档以生成描述响应有效负载中字段的代码段。 要记录请求,您可以使用requestFields. 两者都是org.springframework.restdocs.payload.PayloadDocumentation.
2 期望一个路径为contact.email. 使用静态fieldWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
3 期望一个路径为contact.name.

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

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

如果您不想为所有字段提供详细的文档,则可以记录有效负载的整个子部分。 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("index", responseFields((1)
			subsectionWithPath("contact").description("The user's contact details")))); (1)
1 使用路径记录小节contact.contact.emailcontact.name现在也被视为已被记录下来。 使用静态subsectionWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
WebTest客户端
this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("user",
		responseFields(
			subsectionWithPath("contact").description("The user's contact details")))); (1)
1 使用路径记录小节contact.contact.emailcontact.name现在也被视为已被记录下来。 使用静态subsectionWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("user",
			responseFields(subsectionWithPath("contact").description("The user's contact details")))) (1)
	.when()
	.get("/user/5")
	.then()
	.assertThat()
	.statusCode(is(200));
1 使用路径记录小节contact.contact.emailcontact.name现在也被视为已被记录下来。 使用静态subsectionWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.

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

如果您根本不想记录字段或子部分,则可以将其标记为已忽略。 这可以防止它出现在生成的代码段中,同时避免前面描述的失败。spring-doc.cadn.net.cn

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

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

本节介绍如何使用 JSON 负载中的字段。spring-doc.cadn.net.cn

JSON 字段路径

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

请考虑以下 JSON 有效负载:spring-doc.cadn.net.cn

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

在前面的 JSON 有效负载中,以下路径都存在:spring-doc.cadn.net.cn

路径

aspring-doc.cadn.net.cn

包含bspring-doc.cadn.net.cn

a.bspring-doc.cadn.net.cn

包含 3 个对象的数组spring-doc.cadn.net.cn

['a']['b']spring-doc.cadn.net.cn

包含 3 个对象的数组spring-doc.cadn.net.cn

a['b']spring-doc.cadn.net.cn

包含 3 个对象的数组spring-doc.cadn.net.cn

['a'].bspring-doc.cadn.net.cn

包含 3 个对象的数组spring-doc.cadn.net.cn

a.b[]spring-doc.cadn.net.cn

包含 3 个对象的数组spring-doc.cadn.net.cn

a.b[].cspring-doc.cadn.net.cn

包含字符串的数组onetwospring-doc.cadn.net.cn

a.b[].dspring-doc.cadn.net.cn

字符串threespring-doc.cadn.net.cn

a['e.dot']spring-doc.cadn.net.cn

字符串fourspring-doc.cadn.net.cn

['a']['e.dot']spring-doc.cadn.net.cn

字符串fourspring-doc.cadn.net.cn

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

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

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

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

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

类型 描述

arrayspring-doc.cadn.net.cn

该字段每次出现的值都是一个数组。spring-doc.cadn.net.cn

booleanspring-doc.cadn.net.cn

该字段每次出现的值都是一个布尔值 (truefalse).spring-doc.cadn.net.cn

objectspring-doc.cadn.net.cn

字段每次出现的值都是一个对象。spring-doc.cadn.net.cn

numberspring-doc.cadn.net.cn

该字段每次出现的值都是一个数字。spring-doc.cadn.net.cn

nullspring-doc.cadn.net.cn

该字段每次出现的值为null.spring-doc.cadn.net.cn

stringspring-doc.cadn.net.cn

该字段每次出现的值都是一个字符串。spring-doc.cadn.net.cn

variesspring-doc.cadn.net.cn

该字段在有效负载中多次出现,具有各种不同类型的负载。spring-doc.cadn.net.cn

您还可以使用type(Object)method 开启FieldDescriptor. 结果toString方法的Object在文档中使用。 通常,由JsonFieldType被使用。 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
.andDo(document("index", responseFields(fieldWithPath("contact.email").type(JsonFieldType.STRING) (1)
	.description("The user's email address"))));
1 将字段的类型设置为String.
WebTest客户端
.consumeWith(document("user",
	responseFields(
		fieldWithPath("contact.email")
			.type(JsonFieldType.STRING) (1)
			.description("The user's email address"))));
1 将字段的类型设置为String.
放心
.filter(document("user", responseFields(fieldWithPath("contact.email").type(JsonFieldType.STRING) (1)
	.description("The user's email address"))))
1 将字段的类型设置为String.
XML 负载

本节介绍如何使用 XML 有效负载。spring-doc.cadn.net.cn

XML 字段路径

XML 字段路径使用 XPath 进行描述。 用于下降到子节点。/spring-doc.cadn.net.cn

XML 字段类型

在记录 XML 有效负载时,您必须使用type(Object)method 开启FieldDescriptor. 提供的类型的toString方法。spring-doc.cadn.net.cn

重用字段描述符

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

考虑一个返回一本书的终端节点:spring-doc.cadn.net.cn

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

的路径titleauthortitleauthor分别。spring-doc.cadn.net.cn

现在考虑一个返回书籍数组的终端节点:spring-doc.cadn.net.cn

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

的路径titleauthor[].title[].author分别。 单个 book 和 array of books 之间的唯一区别是字段的路径现在有一个[].前缀。spring-doc.cadn.net.cn

您可以创建记录书籍的描述符,如下所示:spring-doc.cadn.net.cn

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

然后,您可以使用它们来记录一本图书,如下所示:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/books/1").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("book", responseFields(book))); (1)
1 公文titleauthor通过使用现有描述符
WebTest客户端
this.webTestClient.get().uri("/books/1").accept(MediaType.APPLICATION_JSON)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("book",
		responseFields(book))); (1)
1 公文titleauthor通过使用现有描述符
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("book", responseFields(book))) (1)
	.when()
	.get("/books/1")
	.then()
	.assertThat()
	.statusCode(is(200));
1 公文titleauthor通过使用现有描述符

您还可以使用描述符来记录书籍数组,如下所示:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/books").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("book", responseFields(fieldWithPath("[]").description("An array of books")) (1)
		.andWithPrefix("[].", book))); (2)
1 记录数组。
2 公文[].title[].author通过使用前缀为[].
WebTest客户端
this.webTestClient.get().uri("/books").accept(MediaType.APPLICATION_JSON)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("books",
		responseFields(
			fieldWithPath("[]")
				.description("An array of books")) (1)
				.andWithPrefix("[].", book))); (2)
1 记录数组。
2 公文[].title[].author通过使用前缀为[].
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("books", responseFields(fieldWithPath("[]").description("An array of books")) (1)
		.andWithPrefix("[].", book))) (2)
	.when()
	.get("/books")
	.then()
	.assertThat()
	.statusCode(is(200));
1 记录数组。
2 公文[].title[].author通过使用前缀为[].

记录 Request 或 Response Payload 的 Subsection

如果有效负载很大或结构复杂,则记录有效负载的各个部分可能很有用。 REST Docs 允许您通过提取有效负载的子部分,然后对其进行记录来实现此目的。spring-doc.cadn.net.cn

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

请考虑以下 JSON 响应正文:spring-doc.cadn.net.cn

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

您可以生成一个代码段来记录temperature对象,如下所示:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("location", responseBody(beneathPath("weather.temperature")))); (1)
1 生成包含响应正文的子部分的代码片段。 使用静态responseBodybeneathPath方法org.springframework.restdocs.payload.PayloadDocumentation. 要为请求正文生成代码段,您可以使用requestBody代替responseBody.
WebTest客户端
this.webTestClient.get().uri("/locations/1").accept(MediaType.APPLICATION_JSON)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("temperature",
		responseBody(beneathPath("weather.temperature")))); (1)
1 生成包含响应正文的子部分的代码片段。 使用静态responseBodybeneathPath方法org.springframework.restdocs.payload.PayloadDocumentation. 要为请求正文生成代码段,您可以使用requestBody代替responseBody.
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("location", responseBody(beneathPath("weather.temperature")))) (1)
	.when()
	.get("/locations/1")
	.then()
	.assertThat()
	.statusCode(is(200));
1 生成包含响应正文的子部分的代码片段。 使用静态responseBodybeneathPath方法org.springframework.restdocs.payload.PayloadDocumentation. 要为请求正文生成代码段,您可以使用requestBody代替responseBody.

结果是一个包含以下内容的代码段:spring-doc.cadn.net.cn

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

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

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

结果是一个名为request-body-temp.adoc.spring-doc.cadn.net.cn

记录请求或响应的 subsection 的字段

除了记录请求或响应正文的子部分外,您还可以记录特定子部分中的字段。 您可以生成一个代码段来记录temperature对象 (highlow) 如下所示:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("location", responseFields(beneathPath("weather.temperature"), (1)
			fieldWithPath("high").description("The forecast high in degrees celcius"), (2)
			fieldWithPath("low").description("The forecast low in degrees celcius"))));
1 生成一个代码段,描述路径下方响应有效负载子部分中的字段weather.temperature. 使用静态beneathPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
2 记录highlow领域。
WebTest客户端
this.webTestClient.get().uri("/locations/1").accept(MediaType.APPLICATION_JSON)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("temperature",
		responseFields(beneathPath("weather.temperature"), (1)
			fieldWithPath("high").description("The forecast high in degrees celcius"), (2)
			fieldWithPath("low").description("The forecast low in degrees celcius"))));
1 生成一个代码段,描述路径下方响应有效负载子部分中的字段weather.temperature. 使用静态beneathPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
2 记录highlow领域。
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("location", responseFields(beneathPath("weather.temperature"), (1)
			fieldWithPath("high").description("The forecast high in degrees celcius"), (2)
			fieldWithPath("low").description("The forecast low in degrees celcius"))))
	.when()
	.get("/locations/1")
	.then()
	.assertThat()
	.statusCode(is(200));
1 生成一个代码段,用于描述响应有效负载的子部分中的字段 在小路下weather.temperature.使用静态beneathPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
2 记录highlow领域。

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

查询参数

您可以使用queryParameters. 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/users?page=2&per_page=100")) (1)
	.andExpect(status().isOk())
	.andDo(document("users", queryParameters((2)
			parameterWithName("page").description("The page to retrieve"), (3)
			parameterWithName("per_page").description("Entries per page") (4)
	)));
1 执行GETrequest 具有两个参数pageper_page,在查询字符串中。 查询参数应包含在 URL 中,如此处所示,或使用请求生成器的queryParamqueryParams方法。 这paramparams应避免使用方法,因为参数的来源是模棱两可的。
2 配置 Spring REST Docs 以生成描述请求的查询参数的代码段。 使用静态queryParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录pagequery 参数。 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
4 记录per_pagequery 参数。
WebTest客户端
this.webTestClient.get().uri("/users?page=2&per_page=100") (1)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("users", queryParameters((2)
			parameterWithName("page").description("The page to retrieve"), (3)
			parameterWithName("per_page").description("Entries per page") (4)
	)));
1 执行GETrequest 具有两个参数pageper_page,在查询字符串中。
2 配置 Spring REST Docs 以生成描述请求的查询参数的代码段。 使用静态queryParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录page参数。 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
4 记录per_page参数。
放心
RestAssured.given(this.spec)
	.filter(document("users", queryParameters((1)
			parameterWithName("page").description("The page to retrieve"), (2)
			parameterWithName("per_page").description("Entries per page")))) (3)
	.when()
	.get("/users?page=2&per_page=100") (4)
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST Docs 以生成描述请求的查询参数的代码段。 使用静态queryParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
2 记录page参数。 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录per_page参数。
4 执行GETrequest 具有两个参数pageper_page,在查询字符串中。

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

如果您不想记录查询参数,可以将其标记为 ignored。 这可以防止它出现在生成的代码片段中,同时避免上述失败。spring-doc.cadn.net.cn

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

表单参数

您可以使用formParameters. 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(post("/users").param("username", "Tester")) (1)
	.andExpect(status().isCreated())
	.andDo(document("create-user", formParameters((2)
			parameterWithName("username").description("The user's username") (3)
	)));
1 执行POSTrequest 替换为单个 form 参数,username.
2 配置 Spring REST Docs 以生成描述请求的表单参数的片段。 使用静态formParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录username参数。 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
WebTest客户端
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("username", "Tester");
this.webTestClient.post().uri("/users").body(BodyInserters.fromFormData(formData)) (1)
		.exchange().expectStatus().isCreated().expectBody()
		.consumeWith(document("create-user", formParameters((2)
				parameterWithName("username").description("The user's username") (3)
		)));
1 执行POSTrequest 替换为单个 form 参数,username.
2 配置 Spring REST Docs 以生成描述请求的表单参数的片段。 使用静态formParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录username参数。 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
放心
RestAssured.given(this.spec)
	.filter(document("create-user", formParameters((1)
			parameterWithName("username").description("The user's username")))) (2)
	.formParam("username", "Tester")
	.when()
	.post("/users") (3)
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST Docs 以生成描述请求的表单参数的片段。 使用静态formParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
2 记录username参数。 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 执行POSTrequest 替换为单个 form 参数,username.

在所有情况下,结果都是一个名为form-parameters.adoc其中包含一个表,该表描述资源支持的表单参数。spring-doc.cadn.net.cn

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

如果您不想记录表单参数,可以将其标记为已忽略。 这可以防止它出现在生成的代码片段中,同时避免上述失败。spring-doc.cadn.net.cn

您还可以在宽松模式下记录表单参数,其中任何未记录的参数都不会导致测试失败。 为此,请使用relaxedFormParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation. 在记录您只想关注表单参数子集的特定方案时,这可能很有用。spring-doc.cadn.net.cn

路径参数

您可以使用pathParameters. 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/locations/{latitude}/{longitude}", 51.5072, 0.1275)) (1)
	.andExpect(status().isOk())
	.andDo(document("locations", pathParameters((2)
			parameterWithName("latitude").description("The location's latitude"), (3)
			parameterWithName("longitude").description("The location's longitude") (4)
	)));
1 执行GETrequest 具有两个路径参数latitudelongitude.
2 配置 Spring REST Docs 以生成描述请求的 path 参数的代码段。 使用静态pathParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录名为latitude. 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
4 记录名为longitude.
WebTest客户端
this.webTestClient.get().uri("/locations/{latitude}/{longitude}", 51.5072, 0.1275) (1)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("locations",
		pathParameters((2)
			parameterWithName("latitude").description("The location's latitude"), (3)
			parameterWithName("longitude").description("The location's longitude")))); (4)
1 执行GETrequest 具有两个路径参数latitudelongitude.
2 配置 Spring REST Docs 以生成描述请求的 path 参数的代码段。 使用静态pathParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录名为latitude. 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
4 记录名为longitude.
放心
RestAssured.given(this.spec)
	.filter(document("locations", pathParameters((1)
			parameterWithName("latitude").description("The location's latitude"), (2)
			parameterWithName("longitude").description("The location's longitude")))) (3)
	.when()
	.get("/locations/{latitude}/{longitude}", 51.5072, 0.1275) (4)
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST Docs 以生成描述请求的 path 参数的代码段。 使用静态pathParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation.
2 记录名为latitude. 使用静态parameterWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录名为longitude.
4 执行GETrequest 具有两个路径参数latitudelongitude.

结果是一个名为path-parameters.adoc其中包含一个表,该表描述资源支持的路径参数。spring-doc.cadn.net.cn

如果将 MockMvc 与 Spring Framework 6.1 或更早版本一起使用,要使路径参数可用于文档,则必须使用RestDocumentationRequestBuilders而不是MockMvcRequestBuilders.

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

您还可以在宽松模式下记录路径参数,其中任何未记录的参数都不会导致测试失败。 为此,请使用relaxedPathParametersmethod 开启org.springframework.restdocs.request.RequestDocumentation. 在记录您只想关注 path 参数子集的特定方案时,这可能很有用。spring-doc.cadn.net.cn

如果您不想记录 path 参数,可以将其标记为已忽略。 这样做可以防止它出现在生成的代码段中,同时避免前面描述的故障。spring-doc.cadn.net.cn

索取零件

您可以使用requestParts来记录 Multipart 请求的各个部分。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(multipart("/upload").file("file", "example".getBytes())) (1)
	.andExpect(status().isOk())
	.andDo(document("upload", requestParts((2)
			partWithName("file").description("The file to upload")) (3)
	));
1 执行POSTrequest 中包含一个名为file.
2 配置 Spring REST Docs 以生成描述请求部分的代码片段。 使用静态requestPartsmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录名为file. 使用静态partWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
WebTest客户端
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
multipartData.add("file", "example".getBytes());
this.webTestClient.post().uri("/upload").body(BodyInserters.fromMultipartData(multipartData)) (1)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("upload", requestParts((2)
		partWithName("file").description("The file to upload")) (3)
));
1 执行POSTrequest 中包含一个名为file.
2 配置 Spring REST Docs 以生成描述请求部分的代码片段。 使用静态requestPartsmethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 记录名为file. 使用静态partWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
放心
RestAssured.given(this.spec)
	.filter(document("users", requestParts((1)
			partWithName("file").description("The file to upload")))) (2)
	.multiPart("file", "example") (3)
	.when()
	.post("/upload") (4)
	.then()
	.statusCode(is(200));
1 配置 Spring REST Docs 以生成描述请求部分的代码片段。 使用静态requestPartsmethod 开启org.springframework.restdocs.request.RequestDocumentation.
2 记录名为file.使用静态partWithNamemethod 开启org.springframework.restdocs.request.RequestDocumentation.
3 使用名为file.
4 执行POSTrequest 添加到/upload.

结果是一个名为request-parts.adoc,其中包含一个描述资源支持的请求部分的表。spring-doc.cadn.net.cn

在记录请求部分时,如果在请求中使用了未记录的部分,则测试将失败。 同样,如果在请求中找不到记录的段,并且该段尚未标记为可选,则测试也会失败。spring-doc.cadn.net.cn

您还可以在宽松模式下记录请求部分,其中任何未记录的部分都不会导致测试失败。 为此,请使用relaxedRequestPartsmethod 开启org.springframework.restdocs.request.RequestDocumentation. 在记录您只想关注请求部分子集的特定方案时,这可能很有用。spring-doc.cadn.net.cn

如果您不想记录请求部分,则可以将其标记为已忽略。 这可以防止它出现在生成的代码段中,同时避免前面描述的失败。spring-doc.cadn.net.cn

请求部件有效负载

您可以采用与请求有效负载大致相同的方式记录请求部分的有效负载,并支持记录请求部分的正文及其字段。spring-doc.cadn.net.cn

记录请求部件的正文

您可以生成包含请求部分正文的代码段,如下所示:spring-doc.cadn.net.cn

MockMvc
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"))); (1)
1 配置 Spring REST 文档以生成一个代码段,其中包含名为metadata. 使用静态requestPartBodymethod 开启PayloadDocumentation.
WebTest客户端
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
Resource imageResource = new ByteArrayResource("<<png data>>".getBytes()) {

	@Override
	public String getFilename() {
		return "image.png";
	}

};
multipartData.add("image", imageResource);
multipartData.add("metadata", Collections.singletonMap("version",  "1.0"));

this.webTestClient.post().uri("/images").body(BodyInserters.fromMultipartData(multipartData))
	.accept(MediaType.APPLICATION_JSON).exchange()
	.expectStatus().isOk().expectBody()
	.consumeWith(document("image-upload",
			requestPartBody("metadata"))); (1)
1 配置 Spring REST 文档以生成一个代码段,其中包含名为metadata. 使用静态requestPartBodymethod 开启PayloadDocumentation.
放心
Map<String, String> metadata = new HashMap<>();
metadata.put("version", "1.0");
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("image-upload", requestPartBody("metadata"))) (1)
	.when()
	.multiPart("image", new File("image.png"), "image/png")
	.multiPart("metadata", metadata)
	.post("images")
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST 文档以生成一个代码段,其中包含名为metadata. 使用静态requestPartBodymethod 开启PayloadDocumentation.

结果是一个名为request-part-${part-name}-body.adoc,其中包含零件的主体。 例如,记录名为metadata生成一个名为request-part-metadata-body.adoc.spring-doc.cadn.net.cn

记录请求部分的字段

您可以采用与记录请求或响应的字段大致相同的方式记录请求部分的字段,如下所示:spring-doc.cadn.net.cn

MockMvc
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", (1)
			fieldWithPath("version").description("The version of the image")))); (2)
1 配置 Spring REST 文档以生成一个代码段,描述名为metadata. 使用静态requestPartFieldsmethod 开启PayloadDocumentation.
2 期望一个路径为version. 使用静态fieldWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
WebTest客户端
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
Resource imageResource = new ByteArrayResource("<<png data>>".getBytes()) {

	@Override
	public String getFilename() {
		return "image.png";
	}

};
multipartData.add("image", imageResource);
multipartData.add("metadata", Collections.singletonMap("version",  "1.0"));
this.webTestClient.post().uri("/images").body(BodyInserters.fromMultipartData(multipartData))
	.accept(MediaType.APPLICATION_JSON).exchange()
	.expectStatus().isOk().expectBody()
	.consumeWith(document("image-upload",
		requestPartFields("metadata", (1)
			fieldWithPath("version").description("The version of the image")))); (2)
1 配置 Spring REST 文档以生成一个代码段,描述名为metadata. 使用静态requestPartFieldsmethod 开启PayloadDocumentation.
2 期望一个路径为version. 使用静态fieldWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.
放心
Map<String, String> metadata = new HashMap<>();
metadata.put("version", "1.0");
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("image-upload", requestPartFields("metadata", (1)
			fieldWithPath("version").description("The version of the image")))) (2)
	.when()
	.multiPart("image", new File("image.png"), "image/png")
	.multiPart("metadata", metadata)
	.post("images")
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST 文档以生成一个代码段,描述名为metadata. 使用静态requestPartFieldsmethod 开启PayloadDocumentation.
2 期望一个路径为version. 使用静态fieldWithPathmethod 开启org.springframework.restdocs.payload.PayloadDocumentation.

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

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

如果您不想记录字段,可以将其标记为已忽略。 这样做可以防止它出现在生成的代码段中,同时避免上述失败。spring-doc.cadn.net.cn

您还可以在宽松模式下记录字段,其中任何未记录的字段都不会导致测试失败。 为此,请使用relaxedRequestPartFieldsmethod 开启org.springframework.restdocs.payload.PayloadDocumentation. 在记录您只想关注部件 payload 子集的特定场景时,这可能很有用。spring-doc.cadn.net.cn

有关描述字段、记录使用 XML 的有效负载等的更多信息,请参阅记录请求和响应有效负载部分spring-doc.cadn.net.cn

HTTP 标头

您可以使用requestHeadersresponseHeaders分别。 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/people").header("Authorization", "Basic dXNlcjpzZWNyZXQ=")) (1)
	.andExpect(status().isOk())
	.andDo(document("headers", requestHeaders((2)
			headerWithName("Authorization").description("Basic auth credentials")), (3)
			responseHeaders((4)
					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"))));
1 执行GETrequest 替换为Authorization标头。
2 配置 Spring REST Docs 以生成描述请求标头的代码段。 使用静态requestHeadersmethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
3 记录Authorization页眉。 使用静态headerWithNamemethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
4 生成描述响应标头的代码段。 使用静态responseHeadersmethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
WebTest客户端
this.webTestClient
	.get().uri("/people").header("Authorization", "Basic dXNlcjpzZWNyZXQ=") (1)
	.exchange().expectStatus().isOk().expectBody()
	.consumeWith(document("headers",
		requestHeaders((2)
			headerWithName("Authorization").description("Basic auth credentials")), (3)
		responseHeaders((4)
			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"))));
1 执行GETrequest 替换为Authorization标头。
2 配置 Spring REST Docs 以生成描述请求标头的代码段。 使用静态requestHeadersmethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
3 记录Authorization页眉。 使用静态headerWithNamemethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
4 生成描述响应标头的代码段。 使用静态responseHeadersmethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
放心
RestAssured.given(this.spec)
	.filter(document("headers", requestHeaders((1)
			headerWithName("Authorization").description("Basic auth credentials")), (2)
			responseHeaders((3)
					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"))))
	.header("Authorization", "Basic dXNlcjpzZWNyZXQ=") (4)
	.when()
	.get("/people")
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST Docs 以生成描述请求标头的代码段。 使用静态requestHeadersmethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
2 记录Authorization页眉。 使用静态headerWithName方法。
3 生成描述响应标头的代码段。 使用静态responseHeadersmethod 开启org.springframework.restdocs.headers.HeaderDocumentation.
4 使用Authorization标头。

结果是一个名为request-headers.adoc以及一个名为response-headers.adoc. 每个 S 都包含一个描述 Headers 的表。spring-doc.cadn.net.cn

记录 HTTP 标头时,如果在请求或响应中找不到记录的标头,则测试将失败。spring-doc.cadn.net.cn

HTTP Cookie

您可以使用requestCookiesresponseCookies分别。 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/").cookie(new Cookie("JSESSIONID", "ACBCDFD0FF93D5BB"))) (1)
	.andExpect(status().isOk())
	.andDo(document("cookies", requestCookies((2)
			cookieWithName("JSESSIONID").description("Session token")), (3)
			responseCookies((4)
					cookieWithName("JSESSIONID").description("Updated session token"),
					cookieWithName("logged_in")
						.description("Set to true if the user is currently logged in"))));
1 使用JSESSIONID饼干。
2 配置 Spring REST Docs 以生成描述请求的 cookie 的代码段。 使用静态requestCookiesmethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
3 记录JSESSIONID饼干。使用静态cookieWithNamemethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
4 生成描述响应的 Cookie 的代码段。 使用静态responseCookiesmethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
WebTest客户端
this.webTestClient.get()
	.uri("/people")
	.cookie("JSESSIONID", "ACBCDFD0FF93D5BB=") (1)
	.exchange()
	.expectStatus()
	.isOk()
	.expectBody()
	.consumeWith(document("cookies", requestCookies((2)
			cookieWithName("JSESSIONID").description("Session token")), (3)
			responseCookies((4)
					cookieWithName("JSESSIONID").description("Updated session token"),
					cookieWithName("logged_in").description("User is logged in"))));
1 使用JSESSIONID饼干。
2 配置 Spring REST Docs 以生成描述请求的 cookie 的代码段。 使用静态requestCookiesmethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
3 记录JSESSIONID饼干。 使用静态cookieWithNamemethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
4 生成描述响应的 Cookie 的代码段。 使用静态responseCookiesmethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
放心
RestAssured.given(this.spec)
	.filter(document("cookies", requestCookies((1)
			cookieWithName("JSESSIONID").description("Saved session token")), (2)
			responseCookies((3)
					cookieWithName("logged_in").description("If user is logged in"),
					cookieWithName("JSESSIONID").description("Updated session token"))))
	.cookie("JSESSIONID", "ACBCDFD0FF93D5BB") (4)
	.when()
	.get("/people")
	.then()
	.assertThat()
	.statusCode(is(200));
1 配置 Spring REST Docs 以生成描述请求的 cookie 的代码段。 使用静态requestCookiesmethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
2 记录JSESSIONID饼干。 使用静态cookieWithNamemethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
3 生成描述响应的 Cookie 的代码段。 使用静态responseCookiesmethod 开启org.springframework.restdocs.cookies.CookieDocumentation.
4 发送JSESSIONIDcookie 的请求。

结果是一个名为request-cookies.adoc以及一个名为response-cookies.adoc. 每个 Cookie 都包含一个描述 Cookie 的表。spring-doc.cadn.net.cn

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

重用代码段

记录的 API 通常具有一些在其多个资源中通用的功能。 为避免在记录此类资源时重复,您可以重用Snippet配置了 Common 元素。spring-doc.cadn.net.cn

首先,创建Snippet,这描述了常见元素。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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"));

其次,使用此代码段并添加更多特定于资源的描述符。 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
	.andExpect(status().isOk())
	.andDo(document("example", this.pagingLinks.and((1)
			linkWithRel("alpha").description("Link to the alpha resource"),
			linkWithRel("bravo").description("Link to the bravo resource"))));
1 重用pagingLinks Snippetand以添加特定于正在记录的资源的描述符。
WebTest客户端
this.webTestClient.get().uri("/").accept(MediaType.APPLICATION_JSON).exchange()
	.expectStatus().isOk().expectBody()
	.consumeWith(document("example", this.pagingLinks.and((1)
		linkWithRel("alpha").description("Link to the alpha resource"),
		linkWithRel("bravo").description("Link to the bravo resource"))));
1 重用pagingLinks Snippetand以添加特定于正在记录的资源的描述符。
放心
RestAssured.given(this.spec)
	.accept("application/json")
	.filter(document("example", this.pagingLinks.and((1)
			linkWithRel("alpha").description("Link to the alpha resource"),
			linkWithRel("bravo").description("Link to the bravo resource"))))
	.get("/")
	.then()
	.assertThat()
	.statusCode(is(200));
1 重用pagingLinks Snippetand以添加特定于正在记录的资源的描述符。

该示例的结果是,与rel的值first,last,next,previous,alphabravo都有记录。spring-doc.cadn.net.cn

记录约束

Spring REST Docs 提供了许多类,可以帮助您记录约束。 您可以使用ConstraintDescriptions以访问类的约束的描述。 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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

static class UserInput {

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

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

}
1 创建ConstraintDescriptions对于UserInput类。
2 获取name属性的约束条件。 此列表包含两个描述:一个用于NotNullconstraint 和一个用于Size约束。

ApiDocumentation类在 Spring HATEOAS 示例中演示了此功能的实际应用。spring-doc.cadn.net.cn

查找约束

默认情况下,通过使用 Bean Validation 来查找约束Validator. 目前仅支持属性约束。 您可以自定义Validator,用于创建ConstraintDescriptions使用自定义ValidatorConstraintResolver实例。 要完全控制约束解析,您可以使用自己的ConstraintResolver.spring-doc.cadn.net.cn

描述约束

为所有 Bean Validation 3.0 的约束提供了默认描述:spring-doc.cadn.net.cn

还为 Hibernate 中的以下约束提供了默认描述 验证人:spring-doc.cadn.net.cn

要覆盖默认描述或提供新描述,您可以创建基本名称为org.springframework.restdocs.constraints.ConstraintDescriptions. 基于 Spring HATEOAS 的示例包含此类资源包的示例spring-doc.cadn.net.cn

资源包中的每个键都是约束的完全限定名称加上.description. 例如,标准@NotNullconstraint 为jakarta.validation.constraints.NotNull.description.spring-doc.cadn.net.cn

您可以在描述中使用引用约束属性的属性占位符。 例如,@Min约束Must be at least ${value},引用约束的value属性。spring-doc.cadn.net.cn

要更好地控制约束描述解析,您可以创建ConstraintDescriptions使用自定义ResourceBundleConstraintDescriptionResolver. 要获得完全控制权,您可以创建ConstraintDescriptions使用自定义ConstraintDescriptionResolver实现。spring-doc.cadn.net.cn

在生成的代码段中使用约束描述

获得约束的描述后,您可以在生成的代码片段中随意使用它们。 例如,您可能希望将约束描述作为字段描述的一部分。 或者,您也可以将约束作为额外信息包含在请求字段代码段中。 这ApiDocumentationclass 说明了后一种方法。spring-doc.cadn.net.cn

默认代码段

当您记录请求和响应时,会自动生成许多代码段。spring-doc.cadn.net.cn

片段 描述

curl-request.adocspring-doc.cadn.net.cn

包含curl命令,它等效于MockMvc调用。spring-doc.cadn.net.cn

httpie-request.adocspring-doc.cadn.net.cn

包含HTTPie命令,它等效于MockMvc调用。spring-doc.cadn.net.cn

http-request.adocspring-doc.cadn.net.cn

包含等效于MockMvc调用。spring-doc.cadn.net.cn

http-response.adocspring-doc.cadn.net.cn

包含返回的 HTTP 响应。spring-doc.cadn.net.cn

request-body.adocspring-doc.cadn.net.cn

包含已发送的请求的正文。spring-doc.cadn.net.cn

response-body.adocspring-doc.cadn.net.cn

包含返回的响应的正文。spring-doc.cadn.net.cn

您可以配置默认生成的代码段。 有关更多信息,请参阅配置部分spring-doc.cadn.net.cn

使用参数化输出目录

您可以参数化document. 支持以下参数:spring-doc.cadn.net.cn

参数 描述

{方法名称}spring-doc.cadn.net.cn

测试方法的未修改名称。spring-doc.cadn.net.cn

{方法名称}spring-doc.cadn.net.cn

测试方法的名称,使用 kebab-case 格式化。spring-doc.cadn.net.cn

{method_name}spring-doc.cadn.net.cn

测试方法的名称,使用 snake_case 设置格式。spring-doc.cadn.net.cn

{类名}spring-doc.cadn.net.cn

测试类的未修改的简单名称。spring-doc.cadn.net.cn

{类名}spring-doc.cadn.net.cn

测试类的简单名称,使用 kebab-case 格式化。spring-doc.cadn.net.cn

{class_name}spring-doc.cadn.net.cn

测试类的简单名称,使用 snake_case 格式设置。spring-doc.cadn.net.cn

{步骤}spring-doc.cadn.net.cn

在当前测试中对服务进行的调用计数。spring-doc.cadn.net.cn

例如document("{class-name}/{method-name}")在名为creatingANote在 test 类上GettingStartedDocumentation将代码段写入名为getting-started-documentation/creating-a-note.spring-doc.cadn.net.cn

参数化输出目录与@Before方法。 它允许在 setup 方法中配置一次文档,然后在类中的每个测试中重用。 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
@Before
public void setUp() {
	this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
		.apply(documentationConfiguration(this.restDocumentation))
		.alwaysDo(document("{method-name}/{step}/"))
		.build();
}
放心
@Before
public void setUp() {
	this.spec = new RequestSpecBuilder().addFilter(documentationConfiguration(this.restDocumentation))
		.addFilter(document("{method-name}/{step}"))
		.build();
}
WebTest客户端
@Before
public void setUp() {
	this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
		.configureClient()
		.filter(documentationConfiguration(this.restDocumentation))
		.entityExchangeResultConsumer(document("{method-name}/{step}"))
		.build();
}

有了此配置,对您正在测试的服务的每次调用都会生成默认代码段,而无需任何进一步的配置。 查看GettingStartedDocumentation类来查看此功能的实际应用。spring-doc.cadn.net.cn

自定义输出

本节介绍如何自定义 Spring REST Docs 的输出。spring-doc.cadn.net.cn

自定义生成的代码段

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

模板是从 Classpath 中从org.springframework.restdocs.templates分包。 分包的名称由正在使用的模板格式的 ID 确定。 默认模板格式 Asciidoctor 的 ID 为asciidoctor,因此代码片段是从org.springframework.restdocs.templates.asciidoctor. 每个模板都以其生成的代码段命名。 例如,要覆盖curl-request.adocsnippet 中,创建一个名为curl-request.snippetsrc/test/resources/org/springframework/restdocs/templates/asciidoctor.spring-doc.cadn.net.cn

包括额外信息

有两种方法可以提供额外的信息以包含在生成的代码段中:spring-doc.cadn.net.cn

在模板渲染过程中,任何其他属性都可用。 与自定义代码段模板结合使用,这样就可以在生成的代码段中包含额外信息。spring-doc.cadn.net.cn

一个具体的例子是在记录请求字段时添加 constraints 列和 title。 第一步是提供constraints属性,并提供title属性。 以下示例说明如何执行此作:spring-doc.cadn.net.cn

MockMvc
.andDo(document("create-user", requestFields(attributes(key("title").value("Fields for user creation")), (1)
		fieldWithPath("name").description("The user's name")
			.attributes(key("constraints").value("Must not be null. Must not be empty")), (2)
		fieldWithPath("email").description("The user's email address")
			.attributes(key("constraints").value("Must be a valid email address"))))); (3)
1 配置title属性。
2 constraints属性name田。
3 constraints属性email田。
WebTest客户端
.consumeWith(document("create-user",
	requestFields(
		attributes(key("title").value("Fields for user creation")), (1)
		fieldWithPath("name")
			.description("The user's name")
			.attributes(key("constraints").value("Must not be null. Must not be empty")), (2)
		fieldWithPath("email")
			.description("The user's email address")
			.attributes(key("constraints").value("Must be a valid email address"))))); (3)
1 配置title属性。
2 constraints属性name田。
3 constraints属性email田。
放心
.filter(document("create-user", requestFields(attributes(key("title").value("Fields for user creation")), (1)
		fieldWithPath("name").description("The user's name")
			.attributes(key("constraints").value("Must not be null. Must not be empty")), (2)
		fieldWithPath("email").description("The user's email address")
			.attributes(key("constraints").value("Must be a valid email address"))))) (3)
1 配置title属性。
2 constraints属性name田。
3 constraints属性email田。

第二步是提供一个名为request-fields.snippet这包括有关生成的代码片段表中字段的约束的信息,并添加标题。spring-doc.cadn.net.cn

.{{title}} (1)
|===
|Path|Type|Description|Constraints (2)

{{#fields}}
|{{path}}
|{{type}}
|{{description}}
|{{constraints}} (3)

{{/fields}}
|===
1 向表添加标题。
2 添加一个名为 “Constraints” 的新列。
3 包括描述符的constraints属性。