Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Controllerからswaggerを自動生成

概要

  • swaggerを使用してAPIの仕様をドキュメントかしていると、swaggerのメンテナンスがされなくなり、実装とドキュメントがずれていく…
  • そこでspringfoxを使って、コードベースでswaggerを自動生成してみた

使用するライブラリ

ライブラリ バージョン
org.springframework.boot:spring-boot-starter-web 2.4.0
org.springframework.boot:spring-boot-starter-validation 2.4.0
io.springfox:springfox-boot-starter 3.0.0

サンプルAPI

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

SampleController.java
@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;
    }
}

導入

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

build.gradle
dependencies {
    compileOnly('org.projectlombok:lombok')
    annotationProcessor('org.projectlombok:lombok')

    implementation 'org.springframework.boot:spring-boot-starter-web:2.4.0'
    implementation 'org.springframework.boot:spring-boot-starter-validation:2.4.0'

    implementation 'io.springfox:springfox-boot-starter:3.0.0'
}
SwaggerAutoConfiguration
@Configuration
@EnableSwagger2
public class SwaggerAutoConfiguration {
}

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

Screen Shot 2020-11-14 at 14.20.44.png

Screen Shot 2020-11-14 at 14.21.23.png

Screen Shot 2020-11-14 at 14.21.43.png

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

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

Screen Shot 2020-11-14 at 14.22.29.png

詳細な設定

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

トップページの設定

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

SwaggerAutoConfiguration.java
@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()
                ));
    }
}

Screen Shot 2020-11-14 at 15.38.11.png

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

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

Screen Shot 2020-11-14 at 15.39.08.png

APIのタイトル

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

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

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

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

SampleController.java
...
    @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);
    }
...

Screen Shot 2020-11-14 at 18.30.16.png

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;
    }

Screen Shot 2020-11-14 at 20.23.14.png

Screen Shot 2020-11-14 at 20.26.44.png

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

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

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

Screen Shot 2020-11-14 at 20.41.16.png

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

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

@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;
    }

Screen Shot 2020-11-14 at 20.52.16.png

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

@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);
    }

Screen Shot 2020-11-14 at 20.58.42.png

補足

コードから仕様(swagger)を作成するのは本来順番が逆だが、
「実装を変更したけど仕様を修正していなかった」
とか
「仕様には書いていたvalidationが抜けていた」
ということがなくなるので嬉しい!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away