【springfox】Controllerからswaggerを自動生成

swaggerを使用してAPIの仕様をドキュメントかしていると、swaggerのメンテナンスがされなくなり実装とドキュメントがドンドンずれていく、、、なんてこと良くありますよね。。

そんなときコードベースでswaggerを自動生成してくれるspringfoxを見つけました!

この記事では、できるだけswagger.yamlを手動で書いた時と同じクオリティになるよう
springfoxの使い方をまとめていきます!

▼メンテされているライブラリを使いたい場合はこちらをご覧ください

【逆引き解説】SpringDocの使い方

前提

ライブラリバージョン
org.springframework.boot:spring-boot-starter-web2.4.0
org.springframework.boot:spring-boot-starter-validation2.4.0
io.springfox:springfox-boot-starter3.0.0

サンプルAPI

なんとなくユーザーを登録するエンドポイントとユーザーを検索するエンドポイントを用意しました

@RestController
@Validated
public class SampleController {

    @PostMapping(value = "/sample",
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    public void register(@RequestBody @Valid User request) {
        return;
    }

    @GetMapping(value = "/sample", produces = MediaType.APPLICATION_JSON_VALUE)
    public User get(@Pattern(regexp = "[0-9a-f-]{36}") String id) {
        return new User("1234567890", "hogehoge", 30);
    }

    @Value
    public static class User {
        @NotNull
        @Pattern(regexp = "[0-9a-f-]{36}")
        private String id;
        @NotNull
        private String name;
        @NotNull
        @Min(1L)
        @Max(100L)
        private int age;
    }
}

springfox導入

導入はライブラリを入れてConfigurationクラスを実装するだけ!

dependencies {
    ...
    // springfoxの依存を追加する
    implementation 'io.springfox:springfox-boot-starter:3.0.0'
}
@Configuration
@EnableSwagger2
public class SwaggerAutoConfiguration {
}

アプリケーションを起動して、
http://localhost:8080/swagger-ui/
にアクセスするとSwaggerが表示されます!

これだけでも結構いい感じですね!

ちなみにValidationも反映してくれてます😍
※このあともう少しリッチにできます

詳細な設定

ここからは設定を追加して。swaggerを充実させていきます。

トップページの設定

ある程度まとめて設定を入れてみます

@Configuration
@EnableSwagger2
public class SwaggerAutoConfiguration {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("<package>"))
                .paths(PathSelectors.any())
                .build()
                // 200以外でデフォルトで用意されているレスポンスを無効にします
                .useDefaultResponseMessages(false)
                // プロトコルを指定します
                .protocols(Collections.singleton("https"))
                .apiInfo(new ApiInfo(
                        // タイトル
                        "springfoxサンプル",
                        // 説明
                        "springfoxのサンプル",
                        // ドキュメントのバージョン
                        "1.0.0",
                        null,
                        null,
                        null,
                        null,
                        Collections.emptyList()
                ));
    }
}

これで情報がすっきりしました!

バリデーションも少し表現が増えています

APIのタイトル

デフォルトだと「sample-controller」と、コントローラーのクラス名が使われているのでこれを変更してみる
→ @Apiアノテーションでtagsを指定すればOK

@Api(tags = "サンプルAPI")
@RestController
@Validated
public class SampleController {
    ...
}

エンドポイントの説明を変える

デフォルトだとControllerクラスのメソッド名が使われていそうなのでこれを変更してみる
→ @ApiOperationアノテーションでvalueを指定すればOK

...
    @ApiOperation("ユーザー登録")
    @PostMapping(value = "/register",
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    public void register(@RequestBody @Valid User request) {
        return;
    }

    @ApiOperation("ユーザー検索")
    @GetMapping(value = "/get", produces = MediaType.APPLICATION_JSON_VALUE)
    public User get(@Pattern(regexp = "[0-9a-f-]{36}") String id) {
        return new User("1234567890", "hogehoge", 30L);
    }
...

Modelに説明やサンプルを追加する

Modelクラスの各フィールドに@ApiModelPropertyを付与します

  • value: 説明
  • required: 必須かどうか
    • デフォルトがfalseとなっていて
    • @NotNullよりも優先されてしまうので明示的に指定が必要
  • position: 表示順序
    • ageの位置がおかしい😇
  • example: 値のサンプル
    @Value
    public static class User {
        @ApiModelProperty(value = "ID", required = true, position = 1,
                example = "8cdd25ef-b8e4-4964-ab78-2ef5e16b54be")
        @NotNull
        @Pattern(regexp = "[0-9a-f-]{36}")
        private String id;

        @ApiModelProperty(value = "名前", required = true, position = 2,
                example = "hogehoge")
        @NotNull
        private String name;

        @ApiModelProperty(value = "年齢", required = true, position = 2,
                example = "30")
        @NotNull
        @Min(1)
        @Max(100)
        private int age;
    }

共通のエラーレスポンスを追加する

作成したSwaggerAutoConfigurationに追記します。

.apiInfo(...)
.globalResponses(HttpMethod.POST, Arrays.asList(
    new ResponseBuilder()
        .code("400")
        .description("Bad Request")
        .build()));

ボディは指定できない…かな😇

エンドポイントごとのエラーレスポンスを追加する

@ApiResponses@ApiResponseを使用します

  • code: レスポンスコード
  • message: エラーの概要
  • response: レスポンスのModelクラス
    @ApiOperation("ユーザー登録")
    @PostMapping(value = "/register",
            consumes = MediaType.APPLICATION_JSON_VALUE,
            produces = MediaType.APPLICATION_JSON_VALUE
    )
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "成功", response = Void.class),
            @ApiResponse(code = 500, message = "システムエラー", response = ErrorResource.class)
    })
    public void register(@RequestBody @Valid User user) {
        return;
    }

クエリパラメータに説明やサンプルを追加する

@ApiParamアノテーションを使用します

  • name: パラメータ名
    • 指定しないと引数の名前をそのまま使ってくれるので、出番ないかも?
  • value: 説明
  • required: 必須かどうか
  • example: 例
    @ApiOperation("ユーザー検索")
    @GetMapping(value = "/get", produces = MediaType.APPLICATION_JSON_VALUE)
    public User get(
            @ApiParam(value = "ユーザーのID", required = true, example = "8cdd25ef-b8e4-4964-ab78-2ef5e16b54be")
            @Pattern(regexp = "[0-9a-f-]{36}") String id) {
        return new User("1234567890", "hogehoge", 30);
    }

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です