ハイパーメディア

Spring REST Docs は、ハイパーメディアベース [Wikipedia] (英語) の API でリンクをドキュメント化するためのサポートを提供します。次の例は、その使用方法を示しています。

  • MockMvc

  • WebTestClient

import org.junit.jupiter.api.Test;

import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class Hypermedia {

	// Fields

	private MockMvc mockMvc;


	@Test
	void test() throws Exception {
		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)
	}

}
1Spring REST ドキュメントを構成して、レスポンスのリンクを説明するスニペットを生成します。org.springframework.restdocs.hypermedia.HypermediaDocumentation で静的 links メソッドを使用します。
2rel が alpha であるリンクを期待します。org.springframework.restdocs.hypermedia.HypermediaDocumentation で静的 linkWithRel メソッドを使用します。
3rel が bravo であるリンクを期待します。
import org.junit.jupiter.api.Test;

import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;

public class Hypermedia {

	// Fields

	private WebTestClient webTestClient;


	@Test
	void test() {
		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)
	}

}
1Spring REST ドキュメントを構成して、レスポンスのリンクを説明するスニペットを生成します。org.springframework.restdocs.hypermedia.HypermediaDocumentation で静的 links メソッドを使用します。
2rel が alpha であるリンクを期待します。org.springframework.restdocs.hypermedia.HypermediaDocumentation で静的 linkWithRel メソッドを使用します。
3rel が bravo であるリンクを期待します。

その結果、リソースのリンクを説明するテーブルを含む links.adoc という名前のスニペットが作成されます。

レスポンス内のリンクに title がある場合、記述子から説明を省略でき、title が使用されます。説明を省略し、リンクに title がない場合、エラーが発生します。

リンクをドキュメント化する場合、ドキュメント化されていないリンクがレスポンスで見つかった場合、テストは失敗します。同様に、ドキュメント化されたリンクがレスポンスに見つからず、リンクがオプションとしてマークされていない場合も、テストは失敗します。

リンクをドキュメント化したくない場合は、無視するようにマークできます。そうすることで、上記の失敗を回避しながら、生成されたスニペットに表示されなくなります。

ドキュメント化されていないリンクがテストの失敗を引き起こさない、緩和モードでリンクをドキュメント化することもできます。これを行うには、org.springframework.restdocs.hypermedia.HypermediaDocumentation で relaxedLinks メソッドを使用します。これは、リンクのサブセットのみに注目したい特定のシナリオをドキュメント化する場合に役立ちます。

デフォルトでは、次の 2 つのリンク形式が認識されます。

  • 原子: リンクは links という名前の配列にあると予想されます。これは、レスポンスのコンテンツ型が application/json と互換性がある場合にデフォルトで使用されます。

  • HAL: リンクは _links という名前のマップにあると予想されます。これは、レスポンスのコンテンツ型が application/hal+json と互換性がある場合にデフォルトで使用されます。

Atom または HAL 形式のリンクを使用するが、コンテンツ型が異なる場合は、組み込みの LinkExtractor 実装の 1 つを links に提供できます。次の例は、その方法を示しています。

  • MockMvc

  • WebTestClient

import org.junit.jupiter.api.Test;

import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class HypermediaWithSpecificExtractor {

	// Fields

	private MockMvc mockMvc;


	@Test
	void explicitExtractor() throws Exception {
		this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
			.andExpect(status().isOk())
			.andDo(document("index", links(halLinks(), (1)
					linkWithRel("alpha").description("Link to the alpha resource"),
					linkWithRel("bravo").description("Link to the bravo resource"))));
	}

}

org.springframework.restdocs.hypermedia.HypermediaDocumentation で静的 halLinks メソッドを使用します。

import org.junit.jupiter.api.Test;

import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;

import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;

class HypermediaWithSpecificExtractor {

	// Fields

	private WebTestClient webTestClient;


	@Test
	void test() {
		this.webTestClient.get()
			.uri("/")
			.accept(MediaType.APPLICATION_JSON)
			.exchange()
			.expectStatus()
			.isOk()
			.expectBody()
			.consumeWith(document("index", links(halLinks(), (1)
					linkWithRel("alpha").description("Link to the alpha resource"),
					linkWithRel("bravo").description("Link to the bravo resource"))));
	}

}

org.springframework.restdocs.hypermedia.HypermediaDocumentation で静的 halLinks メソッドを使用します。

API が Atom または HAL 以外の形式でリンクを表す場合、LinkExtractor インターフェースの独自の実装を提供して、レスポンスからリンクを抽出できます。

HAL を使用する場合の self や curies など、すべてのレスポンスに共通するリンクをドキュメント化するのではなく、概要セクションで一度ドキュメント化してから、残りの API ドキュメントで無視することをお勧めします。そのために、スニペットを再利用するためのサポートを構築して、特定のリンクを無視するように事前構成されたスニペットにリンク記述子を追加できます。次の例は、その方法を示しています。

import org.springframework.restdocs.hypermedia.HypermediaDocumentation;
import org.springframework.restdocs.hypermedia.LinkDescriptor;
import org.springframework.restdocs.hypermedia.LinksSnippet;

import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;

final class Hypermedia {

	// @fold: on
	private Hypermedia() {

	}
	// @fold: off

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

}