SpringBoot(gradle)にOpenAPI(Swagger)の導入をしていろいろ設定してみた

## はじめに
昨年[精算くんという個人開発のアプリケーション](https://seisan-kun.netlify.app/#/)を作成したのですが、バックエンドAPIの仕様書を作成していなかったので、手っ取り早くOpenAPI(Swagger)を利用して作成していこうと思い利用するに至りました。
その際の導入方法や、個人的に気になった設定について触れていきます。

## 環境
 - Java 11.0.3 (Amazon Corretto)
 - gradle 6.0.1
 - SpringBoot 2.2.3
 - Seisankun-api https://github.com/nooboolean/seisankun_api
 - SwaggerUI 2.9.2

## OpenAPI(Swagger)とは
>The OpenAPI Specification, originally known as the Swagger Specification, is a specification for machine-readable interface files for describing, producing, consuming, and visualizing RESTful web services.
>
> (翻訳)OpenAPI仕様とは、RESTful Webサービスを記述、生成、消費、および視覚化するための機械可読インターフェースファイルの仕様です。

[wikipedia](https://en.wikipedia.org/wiki/OpenAPI_Specification)より

ということで、OpenAPIとは簡単にいうと、RESTfulなWebサービス(APIなど)の仕様書作成のためのインターフェースです。
もともとはSwaggerという名称でしたが、2015年12月31日に、Microsoft/Google/IBMなどにより設立されたOAI(Open API Initiative)に寄贈されたため、名称が変わってOpenAPIとなったようです。
OAIにはSwaggerの生みの親であるSmartBear Software社も参加しており、寄贈に至ったそうです。
ただし、OpenAPIがまだまだ呼び名として浸透されていないため、依然Swaggerと呼ぶ人や記事が存在します。

## SpringBootにOpenAPI(Swagger)を導入
SpringBootにOpenAPIを導入する方法はいくつかあります。
その中でも今回は `SpringFox`を使用して導入します。
`SpringFox`とはOpenAPIベースの仕様ドキュメントを作成できるSpring Frameworkのライブラリです。
https://github.com/springfox/springfox

### gradleに定義
build.gradleに以下記述を記載します。

```gradle:build.gradle
~省略~
dependencies {
        compile("io.springfox:springfox-swagger2:${お好みのバージョン}")
        compile("io.springfox:springfox-swagger-ui:${お好みのバージョン}")
~省略~
}
```

`${お好みのバージョン}` には使用したいバージョンを指定してください。以下リンクに指定できるバージョンとその使用率などが記載されています。

[springfox-swagger2](https://mvnrepository.com/artifact/io.springfox/springfox-swagger2)
[springfox-swagger-ui](https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui)

### Configureクラスの作成
SpringBootのAutoConfigureの機能を使って、設定を読み込ませていきます。
以下のConfigureクラスを作成します。

```java:SwaggerConf.java
package com.dededesignworkshop.seisankun_api.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConf {

    @Bean
    public Docket swaggerPlugin() {
        return new Docket(DocumentationType.SWAGGER_2) // SWAGGER_12, SWAGGER_2, SWAGGER_WEBがあるがどれが良いのかあまりわからない
                .select() //ApiSelectorBuilderの生成
                .apis(RequestHandlerSelectors.any()) //ドキュメントの対象となるRequestHandlerを設定
                .paths(PathSelectors.regex("/v1.*")) //ドキュメントの対象となるPathを設定
                .build();
    }

}

```
DocketというクラスをBean定義しています。
公式ドキュメントは[こちら](http://springfox.github.io/springfox/javadoc/current/springfox/documentation/spring/web/plugins/Docket.html#select())ですので、他の設定項目も見てみてください。

### ブラウザ表示
`/swagger-ui.html`のパスへアクセスすると以下の表示になります。
![スクリーンショット 2021-01-01 23.41.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183984/f463eb21-a92a-1c35-7aab-e2755b950495.png)



## いろいろな設定
最低限の設定は上記のBean定義のみで問題ないんですが、他にもいろいろカスタマイズができますので、試してみました。
### APIの基本情報設定
DocketのapiInfo()でAPIの基本情報設定が可能です。
画像の赤枠の部分です。
![スクリーンショット 2021-01-02 0.08.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183984/48b741fd-fbd4-f6ad-a0e2-44391bce9bee.png)


先程のConfigureクラスに以下を追加してください。

```java:SwaggerConf.java
~省略~

@Configuration
@EnableSwagger2
public class SwaggerConf {

    @Bean
    public Docket swaggerPlugin() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.regex("/v1.*"))
                .build()
                .apiInfo(getApiInfo()); //ApiInfo型でAPIの基本情報設定
    }

    private ApiInfo getApiInfo() {
        return new ApiInfoBuilder()
                .title("Seisankun-api")
                .description("旅行の割り勘計算アプリ\"清算くん\"のAPI")
                .version("1.0.0")
                .contact(new Contact("nooboolean","webSiteUrl","email"))
                .build();
    }
}
```

公式ドキュメントは[こちら](http://springfox.github.io/springfox/javadoc/2.9.2/index.html?springfox/documentation/builders/ApiInfoBuilder.html)ですので、他の設定項目も見てみてください。

### 各エンドポイントの詳細設定
UiConfigurationBuilderでUiConfigurationクラスを返すBean定義をすることで、 各エンドポイントの詳細設定が可能です。
以下の画面の設定を行えます。
![スクリーンショット 2021-01-02 0.42.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183984/11376d28-f5e9-7bf3-0c79-569ab85fd53e.png)


先程のConfigureクラスに以下を追加してください。

```java:SwaggerConf.java
~省略~

@Configuration
@EnableSwagger2
public class SwaggerConf {

~省略~

    @Bean
    public UiConfiguration swaggerUiConf() {
        return UiConfigurationBuilder.builder()
                .displayRequestDuration(true)
                .defaultModelExpandDepth(3)
                .defaultModelRendering(ModelRendering.EXAMPLE)
                .filter(true)
                .operationsSorter(OperationsSorter.METHOD)
                .validatorUrl("")
                .docExpansion(DocExpansion.LIST)
                .build();
    }
}

```
今回設定した項目の詳細です。

| 設定項目 | 説明 |
|:-----------|:------------|
| displayRequestDuration | OpenAPI上でAPI実行した際のRequestDuration(レスポンスタイム)の表示有無を設定できます。 |
| defaultModelExpandDepth| Responses の詳細に記載される、レスポンスの具体的な値の構造(Model)を表記する際に、どの階層まで折りたたまずに開いておくかを設定できます。 |
| defaultModelRendering| Responses の詳細に記載される、レスポンスの具体的な値のデフォルト表記を、「値の例」にするか「構造(Model)」にするかを選べます。 |
| filter | エンドポイントのフィルタリングができるフィルターフィールドの必要有無が設定できます。 |
| operationsSorter | エンドポイントの並び順を設定できます。今回はメソッド順になるようにしています。 |
| docExpansion | 階層的になっているエンドポイントの表示をデフォルトでどこまで開くかを設定できます。 |
| validatorUrl | validationUrlを設定できます。無効にすることで[このような](https://iikanji.hatenablog.jp/entry/2018/04/14/222537)問題を回避できます。 |

公式ドキュメントは[こちら](https://springfox.github.io/springfox/javadoc/current/springfox/documentation/swagger/web/UiConfigurationBuilder.html)ですので、他の設定項目も見てみてください。

### 各エンドポイントの説明の設定
各エンドポイントがどんな役割かなどの説明を記載することができます。
各々のエンドポイントのメソッドにApiOperationアノテーションをつけることで可能です。

```java:UserController.java

    @ApiOperation(value = "旅行に紐づくユーザリストを参照します", notes = "パラメータで指定した旅行IDに紐づくユーザリストを参照します")
    public List<User> findByTravelId(@PathVariable("travel_id") Integer travel_id) {
        return this.userService.findByTravelId(travel_id);
    }
```

表示結果は以下の通りです。
![スクリーンショット 2021-01-02 1.24.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183984/200570a1-3e23-dcb0-784e-51cc03974f2b.png)


他にもまだまだカスタマイズできそうなので、追加でカスタマイズした際はその都度追記していこうと思います！！