【Java】SwaggerでAPI仕様を見れるところまでやってみた（swagger-core × springdoc-openapi-ui）

# まえがき
新しい現場でSwaggerを使うことになったので、備忘録として残す。

# やりたいこと
既存のソースコードにSwaggerで提供されているアノテーションをつけて、Swagger UI PageにてAPI仕様を見たい。

# 仕様(OpenAPI)に対する実装(Swagger)
ぐぐると最初にOpenAPI(Swagger)と出てきた。仕様(OpenAPI)に対する実装(Swagger)という解釈で問題なさそう。JPAとHibernateの関係と同じか。

>OpenAPI = Specification(仕様)
>Swagger = Tools for implementing the specification(実装)

https://swagger.io/blog/api-strategy/difference-between-swagger-and-openapi/

# SpringFox と springdoc-openapi
Swagger UI Pageを見るために必要なもの。

　古：SpringFox
　新：springdoc-openapi

https://qiita.com/yukithm/items/fafc54bc331696b0c333

この記事では新しい方の`springdoc-openapi`を使っていく。

https://springdoc.org/

:::note info
**2024/01/05追記
`swagger-core`と`springdoc-openapi-ui`の２つを定義する方法では SwaggerUIを開くことができないことを検知。
[公式サイトのGetting Started](https://springdoc.org/#getting-started)を参照したところ、`springdoc-openapi-starter-webmvc-ui`の依存を追加するやり方に変更されていたので、本記事もそれに倣って修正する。**
:::

# pom.xml
```xml
<dependencies>

   <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
      <version>2.3.0</version>
   </dependency>

</dependencies>
```

# とりあえずSpringBootアプリ起動して、Swagger UI Pageを開いてみる
`http://server:port/context-path/swagger-ui.html`にアクセスすれば、Swagger UI Pageが見れるそうなので、SpringBootアプリを起動してさっそくhttp://localhost:8080/swagger-ui.html にアクセスしてみた。

まず、Controllerクラスに定義されているエンドポイント一覧が表示されていることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/b4af9aa7-664e-ca7f-8c3f-9b5fae40dfbf.png)

各エンドポイントのリクエスト/レスポンスに設定されているオブジェクトも自動的に一覧で表示してくれるようだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/cc064976-cb4d-ce2a-6283-71bbbbe04667.png)

中身を見てみると、各リクエスト/レスポンスで定義されているフィールドとそのデータ型を見れるようだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/faf29782-f271-0666-9bd9-9f7df9c3ff89.png)


現時点では、swagger-coreで提供されているアノテーションを何もつけていないのだが、
上述したように下記については自動で仕様としておこしてくれるようだ。
・Controllerクラスに定義したエンドポイント
・エンドポイントのリクエスト/レスポンス


# swagger-coreのアノテーションをつけていく
## Controllerクラスにつける
まず、Controllerクラス上部に@ApiResponsesをつけてみる。
```java
@ApiResponses(
   {
    @ApiResponse(
       responseCode = "500", 
       description = "Internal server error",
       content = @Content(schema = @Schema(implementation ErrorResponse.class))),

    @ApiResponse(
       responseCode = "400", 
       description = "Bad request",
       content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
   }
)
public class SampleUserController {
```
再度Swagger UI Pageを開いてみると、エンドポイントのレスポンスに200だけデフォルトで定義されていたものが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/ba80d227-97b4-e837-9722-4ceebf408d1d.png)
400と500のレスポンス情報が定義されるようになった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/37b80c89-6a21-46c7-4254-88390016ffb1.png)
## Controllerクラス内の各エンドポイントにつける
次に、各エンドポイントにもつけてみよう。
```java
  @Operation(description = "Save sample user data")
  @ApiResponses(value = {
      @ApiResponse(responseCode = "200", description = "OK",
          content = @Content(
              schema = @Schema(implementation = SampleUserSaveResponse.class))),
  })
  @PostMapping("save")
  public ResponseEntity<SampleUserSaveResponse> save(@RequestBody SampleUserSaveRequest request) {
    return ResponseEntity.ok(sampleUserService.save(request));
  }
```
再度Swagger UI Pageを開いてみると、@Operationsに定義した説明文が↓のように追加されており、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/3db05a6d-c941-4faf-129d-50c1d478055d.png)
Responseについては、デフォルトの200が消えてしまっていたので再度追加したものが反映されていることを確認できた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/c90b59f1-42d9-ec0d-a967-700f0d714a2a.png)
## リクエスト/レスポンスクラスにつけてみる
次に、エンドポイントから参照されているリクエスト/レスポンスにもつけてみよう。

リクエスト
```java
@Schema(description = "Sample user save request model")
public class SampleUserSaveRequest {

  @Schema(description = "ユーザ名", type = "string", example = "Anthony")
  private String name;
  @Schema(description = "年齢", type = "integer", example = "24")
  private Integer age;
  @Schema(description = "性別", type = "string", example = "male")
  private String gender;
  @Schema(description = "生年月日", type = "string", example = "1990-01-01")
  private String birthDate;
  @Schema(description = "作成日時", type = "string", example = "2022-01-01 12:34:56")
  private String createdAt;
  @Schema(description = "更新日時", type = "string", example = "2022-01-01 12:34:56")
  private String updatedAt;
}
```

レスポンス
```java
@Schema(description = "Sample user save response model")
public class SampleUserSaveResponse {
  private SampleUserDto sampleUser;
}

public class SampleUserDto {
  private Integer userId;
  @Schema(description = "ユーザ名", type = "string", example = "Anthony")
  private String name;
  @Schema(description = "年齢", type = "integer", example = "24")
  private Integer age;
  @Schema(description = "性別", type = "string", example = "male")
  private String gender;
  @Schema(description = "生年月日", type = "string", example = "1990-01-01")
  private String birthDate;
  @Schema(description = "作成日時", type = "string", example = "2022-01-01 12:34:56")
  private String createdAt;
  @Schema(description = "更新日時", type = "string", example = "2022-01-01 12:34:56")
  private String updatedAt;
  @Schema(description = "バージョン", type = "long", example = "0")
  private Long version;
}
```

アノテーションをつける前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/3f373fef-a72b-3201-1e95-0056d95b299f.png)
アノテーションをつけた後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/19ca4134-eec0-64e3-08e3-be3aded65e31.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/2be21798-26f5-a805-b2d5-28da37026d04.png)


## swagger-coreアノテーションについて

### @ApiResponse, @ApiResponses
200以外のレスポンスコードが返る場合の説明を書きます。

### @Content
parameter, request, responseのcontentTypeを定義する。
contentTypeをスキーマで表現したかったら、@Content#schemaを使う？

https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations#content

### @Schema

スキーマとは何者？
・クラス、フィールドなど、**設計書におこしたいデータを１つの概念としてあらわしたもの**

クラスにつける場合
```java
@Schema(description = "Hoge search request model")
public class HogeSearchRequestParameter {}
```

フィールドにつける場合
```java
@Schema(description = "birthDate",
      type = "string",
      example = "202110",
      required = true)
private String birthDate;
```
### @Operation
resource methodにつける。どういったデータを取得できるのかを説明する。

https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations#operation

# その他単語

## OAS
OpenAPI Specfication