SpringRESTドキュメントとOpenAPI
1. 概要
Spring RESTDocsとOpenAPI3.0は、RESTAPIのAPIドキュメントを作成する2つの方法です。
このチュートリアルでは、それらの相対的な長所と短所を調べます。
2. 起源の簡単な要約
Spring REST Docs は、RESTfulAPIの正確なドキュメントを作成するためにSpringコミュニティによって開発されたフレームワークです。 テスト駆動型のアプローチを採用しており、ドキュメントはSpring MVCテスト、SpringWebfluxのWebTestClient、、またはREST-Assuredのいずれかとして記述されています。
テスト実行の出力はAsciiDocファイルとして作成され、 Asciidoctor を使用してまとめて、APIを説明するHTMLページを生成できます。 TDD方式に従っているため、Spring REST Docsは、エラーが発生しにくいコード、やり直しの削減、フィードバックサイクルの高速化など、すべての利点を自動的にもたらします。
一方、 OpenAPI は、Swagger2.0から生まれた仕様です。 これを書いている時点での最新バージョンは3.0であり、多くの既知の実装があります。
他の仕様と同様に、OpenAPIは、その実装が従うべき特定の基本ルールを定めています。 簡単に言えば、すべての OpenAPI実装は、JSONまたはYAML形式のいずれかでドキュメントをJSONオブジェクトとして生成することになっています。
このJSON/YAMLを取り込んでUIを吐き出し、APIを視覚化してナビゲートする多くのツールもあります。 これは、たとえば、検収試験の際に役立ちます。 ここでのコードサンプルでは、 springdoc –Spring Bootを備えたOpenAPI3のライブラリを使用します。
2つを詳細に説明する前に、文書化するAPIを簡単に設定しましょう。
3. RESTAPI
Spring Bootを使用して基本的なCRUDAPIをまとめましょう。
3.1. リポジトリ
ここで使用するリポジトリは、モデルFooを備えた最低限のPagingAndSortingRepositoryインターフェイスです。
@Repository
public interface FooRepository extends PagingAndSortingRepository<Foo, Long>{}
@Entity
public class Foo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false)
private String title;
@Column()
private String body;
// constructor, getters and setters
}
また、schema.sqlとdata.sqlを使用してリポジトリをロードします。
3.2. コントローラー
次に、簡潔にするために実装の詳細をスキップして、コントローラーを見てみましょう。
@RestController
@RequestMapping("/foo")
public class FooController {
@Autowired
FooRepository repository;
@GetMapping
public ResponseEntity<List<Foo>> getAllFoos() {
// implementation
}
@GetMapping(value = "{id}")
public ResponseEntity<Foo> getFooById(@PathVariable("id") Long id) {
// implementation
}
@PostMapping
public ResponseEntity<Foo> addFoo(@RequestBody @Valid Foo foo) {
// implementation
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteFoo(@PathVariable("id") long id) {
// implementation
}
@PutMapping("/{id}")
public ResponseEntity<Foo> updateFoo(@PathVariable("id") long id, @RequestBody Foo foo) {
// implementation
}
}
3.3. アプリケーション
そして最後に、ブートアプリ:
@SpringBootApplication()
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4. OpenAPI / Springdoc
次に、springdocがFooRESTAPIにドキュメントを追加する方法を見てみましょう。
JSONオブジェクトとそのオブジェクトに基づくAPIのUI視覚化を生成することを思い出してください。
4.1. 基本的なUI
まず、Mavenの依存関係をいくつか追加します。JSONを生成するための springdoc-openapi-data-rest と、UIをレンダリングするためのspringdoc-openapi-uiです。
このツールは、APIのコードをイントロスペクトし、コントローラーメソッドのアノテーションを読み取ります。 これに基づいて、 http:// localhost:8080 / api-docs/で公開されるAPIJSONが生成されます。 また、 http:// localhost:8080 /swagger-ui-custom.htmlで基本的なUIを提供します。
ご覧のとおり、コードをまったく追加しなくても、Fooスキーマに至るまでAPIの美しい視覚化が得られました。 試してみるボタンを使用すると、操作を実行して結果を表示することもできます。
さて、 APIに実際のドキュメントを追加したい場合はどうなりますか? APIとは何か、そのすべての操作の意味、入力する必要があるもの、期待される応答についてはどうでしょうか。
これについては、次のセクションで説明します。
4.2. 詳細なUI
まず、APIに一般的な説明を追加する方法を見てみましょう。
そのために、OpenAPIBeanをブートアプリに追加します。
@Bean
public OpenAPI customOpenAPI(@Value("${springdoc.version}") String appVersion) {
return new OpenAPI().info(new Info()
.title("Foobar API")
.version(appVersion)
.description("This is a sample Foobar server created using springdocs - " +
"a library for OpenAPI 3 with spring boot.")
.termsOfService("http://swagger.io/terms/")
.license(new License().name("Apache 2.0")
.url("http://springdoc.org")));
}
次に、API操作にいくつかの情報を追加するために、いくつかのOpenAPI固有のアノテーションでマッピングを装飾します。
getFooById。を説明する方法を見てみましょう。これは別のコントローラーFooBarController内で行います。これは、FooControllerに似ています。
@RestController
@RequestMapping("/foobar")
@Tag(name = "foobar", description = "the foobar API with documentation annotations")
public class FooBarController {
@Autowired
FooRepository repository;
@Operation(summary = "Get a foo by foo id")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "found the foo", content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = Foo.class))}),
@ApiResponse(responseCode = "400", description = "Invalid id supplied", content = @Content),
@ApiResponse(responseCode = "404", description = "Foo not found", content = @Content) })
@GetMapping(value = "{id}")
public ResponseEntity getFooById(@Parameter(description = "id of foo to be searched")
@PathVariable("id") String id) {
// implementation omitted for brevity
}
// other mappings, similarly annotated with @Operation and @ApiResponses
}
次に、UIへの影響を見てみましょう。
したがって、これらの最小限の構成で、APIのユーザーは、APIの内容、使用方法、および期待される結果を確認できます。 コードをコンパイルしてブートアプリを実行するだけで済みました。
5. SpringRESTドキュメント
RESTドキュメントは、APIドキュメントに対するまったく異なる考え方です。 前に説明したように、プロセスはテスト駆動型であり、出力は静的HTMLページの形式です。
この例では、SpringMVCテストを使用してドキュメントスニペットを作成します。
最初に、spring-restdocs-mockmvc依存関係とasciidocMavenプラグインをpomに追加する必要があります。
5.1. JUnit5テスト
次に、ドキュメントを含むJUnit5テストを見てみましょう。
@ExtendWith({ RestDocumentationExtension.class, SpringExtension.class })
@SpringBootTest(classes = Application.class)
public class SpringRestDocsIntegrationTest {
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@BeforeEach
public void setup(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation))
.build();
}
@Test
public void whenGetFooById_thenSuccessful() throws Exception {
ConstraintDescriptions desc = new ConstraintDescriptions(Foo.class);
this.mockMvc.perform(get("/foo/{id}", 1))
.andExpect(status().isOk())
.andDo(document("getAFoo", preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
pathParameters(parameterWithName("id").description("id of foo to be searched")),
responseFields(fieldWithPath("id")
.description("The id of the foo" +
collectionToDelimitedString(desc.descriptionsForProperty("id"), ". ")),
fieldWithPath("title").description("The title of the foo"),
fieldWithPath("body").description("The body of the foo"))));
}
// more test methods to cover other mappings
}
このテストを実行した後、指定されたAPI操作に関する情報を含むいくつかのファイルを targets/generated-snippetsディレクトリに取得します。 特に、 whenGetFooById_thenSuccessful は、ディレクトリ内のgetAFooフォルダに8つのadocを提供します。
これがサンプルhttp-response.adocで、もちろん応答本文が含まれています。
[source,http,options="nowrap"]
----
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 60
{
"id" : 1,
"title" : "Foo 1",
"body" : "Foo body 1"
}
----
5.2. fooapi.adoc
次に、これらすべてのスニペットを組み合わせて、適切に構造化されたHTMLを形成するマスターファイルが必要です。
それをfooapi.adocと呼び、その一部を見てみましょう。
=== Accessing the foo GET
A `GET` request is used to access the foo read.
==== Request structure
include::{snippets}/getAFoo/http-request.adoc[]
==== Path Parameters
include::{snippets}/getAFoo/path-parameters.adoc[]
==== Example response
include::{snippets}/getAFoo/http-response.adoc[]
==== CURL request
include::{snippets}/getAFoo/curl-request.adoc[]
asciidoctor-maven-pluginを実行した後、target/generated-docsフォルダーに最終的なHTMLファイルfooapi.htmlを取得します。
そして、これはブラウザで開いたときにどのように見えるかです:
6. 重要なポイント
両方の実装を確認したので、長所と短所を要約してみましょう。
springdoc 、を使用すると、RESTコントローラーのコードが乱雑になり、読みやすさが低下しました。 また、ドキュメントはコードと緊密に結合されており、本番環境に移行します。
言うまでもなく、ここでのドキュメントの維持は別の課題です。APIの何かが変更された場合、プログラマーは常に対応するOpenAPIアノテーションを更新することを忘れないでしょうか。
一方、 REST Docsは、他のUIほどキャッチーに見えず、受け入れテストに使用することもできません。 しかし、それには利点があります。
特に、 Spring MVCテストが正常に完了すると、スニペットが得られるだけでなく、他の単体テストと同様にAPIが検証されます。 これにより、APIの変更に対応するドキュメントの変更が必要になります。 また、ドキュメントコードは実装から完全に分離されています。
しかし、逆に言えば、ドキュメントを生成するためにさらにコードを作成する必要がありました。 まず、OpenAPIアノテーションとほぼ同じくらい冗長なテスト自体、そして次に、マスターadocです。
また、最終的なHTMLを生成するには、さらに多くの手順が必要です。最初にテストを実行し、次にプラグインを実行します。 Springdoc では、ブートアプリを実行するだけで済みました。
7. 結論
このチュートリアルでは、OpenAPIベースのspringdocとSpringRESTDocsの違いを確認しました。 また、この2つを実装して、基本的なCRUDAPIのドキュメントを生成する方法についても説明しました。
要約すると、どちらにも長所と短所があり、どちらを使用するかの決定は、特定の要件に従う必要があります。
いつものように、ソースコードはGitHubでから入手できます。