Skip to content

springdoc で API ドキュメント作成

API 設計書の作成方法について説明します。springdoc-openapi によって swagger を自動生成するようにしています。

cf. https://springdoc.org/

実装

pom.xml

下記の依存関係を追加します:

        <!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.8.13</version>
        </dependency>

application.properties

下記設定を追記します:

# swagger出力用ymlファイルのエンドポイント
springdoc.api-docs.path=/api-docs
# swaggerドキュメント閲覧用エンドポイント
springdoc.swagger-ui.path=/swagger-ui.html
# application-swagger.ymlを読み込ませる
spring.profiles.active=swagger

EasyappApplication.java

下記アノテーションを追記し、API の概要を記載します:

  package nob.example.easyapp;

  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;

+ import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+ import io.swagger.v3.oas.annotations.info.Info;

  @SpringBootApplication
+ @OpenAPIDefinition(info = @Info(title = "Easy App", version = "1.0.0", description = "サンプルのREST APIです。"))
  public class EasyappApplication {

      public static void main(String[] args) {
          SpringApplication.run(EasyappApplication.class, args);
      }
  }

AuthController.java

下記アノテーションを追記し、各 API のインターフェース仕様を記載します:

  package nob.example.easyapp.controller;

+ import org.springdoc.core.annotations.ParameterObject;
  import org.springframework.web.bind.annotation.GetMapping;
  import org.springframework.web.bind.annotation.PostMapping;
  import org.springframework.web.bind.annotation.RequestBody;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RestController;

+ import io.swagger.v3.oas.annotations.Operation;
+ import io.swagger.v3.oas.annotations.media.Content;
+ import io.swagger.v3.oas.annotations.media.Schema;
+ import io.swagger.v3.oas.annotations.responses.ApiResponse;
+ import io.swagger.v3.oas.annotations.responses.ApiResponses;
+ import io.swagger.v3.oas.annotations.tags.Tag;
  import nob.example.easyapp.controller.model.LoginRequest;
  import nob.example.easyapp.controller.model.LoginResponse;
  import nob.example.easyapp.controller.model.MeRequest;
  import nob.example.easyapp.controller.model.MeResponse;
  import nob.example.easyapp.exception.SampleException;
+ import nob.example.easyapp.handler.SampleExceptionHandler.SampleExceptionResponseBody;

  /**
   * 認証コントローラーのインターフェースです。
   *
   * @author nob
   */
  @RestController
  @RequestMapping(value = "/api/v1")
+ @Tag(name = "Auth", description = "認証APIです。")
  public interface AuthController {

      /**
       * 認証処理を呼び出します。
       *
       * @param request 認証リクエスト
       * @return 認証結果
       */
      @PostMapping(value = "/login")
+     @Operation(summary = "認証", description = "${easyappdoc.describe.api.v1.login:説明文}")
+     @ApiResponses(value = {
+             @ApiResponse(responseCode = "200", description = "正常に処理された場合"),
+             @ApiResponse(responseCode = "422", description = "エラーが発生した場合", content = @Content(schema = @Schema(implementation = SampleExceptionResponseBody.class)))
+     })
      LoginResponse login(@RequestBody LoginRequest request) throws SampleException;

      /**
       * ユーザ情報取得処理を呼び出します。
       *
       * @param request ユーザ情報取得リクエスト
       * @return ユーザ情報
       */
      @GetMapping(value = "/me")
+     @Operation(summary = "ユーザ情報取得", description = "${easyappdoc.describe.api.v1.me:説明文}")
+     @ApiResponses(value = {
+             @ApiResponse(responseCode = "200", description = "正常に処理された場合")
+     })
-     MeResponse me(MeRequest request);
+     MeResponse me(@ParameterObject MeRequest request);
  }

model

各モデルクラスのスキーマ定義を記載します:

LoginRequest.java

  package nob.example.easyapp.controller.model;

+ import io.swagger.v3.oas.annotations.media.Schema;
  import lombok.Value;

  /**
   * 認証向けのリクエストモデルです。
   *
   * @author nob
   */
  @Value
+ @Schema(description = "認証向けのリクエストモデル", type = "object")
  public class LoginRequest {

      /** ユーザ名 */
+     @Schema(description = "ユーザ名", type = "string", example = "nob")
      private String name;

      /** パスワード */
+     @Schema(description = "パスワード", type = "string", example = "passwd")
      private String password;
  }

LoginResponse.java

  package nob.example.easyapp.controller.model;

+ import io.swagger.v3.oas.annotations.media.Schema;
  import lombok.Value;

  /**
   * 認証向けのレスポンスモデルです。
   *
   * @author nob
   */
  @Value
+ @Schema(description = "認証向けのレスポンスモデル", type = "object")
  public class LoginResponse {

      /** 認証可否 */
+     @Schema(description = "認証可否", type = "boolean", example = "true")
      private boolean valid;
  }

MeRequest.java

  package nob.example.easyapp.controller.model;

+ import io.swagger.v3.oas.annotations.media.Schema;
  import lombok.Value;

  /**
   * ユーザ情報取得向けのリクエストモデルです。
   *
   * @author nob
   */
  @Value
+ @Schema(description = "ユーザ情報取得向けのリクエストモデル", type = "object")
  public class MeRequest {

      /** ユーザ名 */
+     @Schema(description = "ユーザ名", type = "string", example = "nob")
      private String name;
  }

MeResponse.java

  package nob.example.easyapp.controller.model;

+ import io.swagger.v3.oas.annotations.media.Schema;
  import lombok.Value;

  /**
   * ユーザ情報取得向けのレスポンスモデルです。
   *
   * @author nob
   */
  @Value
+ @Schema(description = "ユーザ情報取得向けのレスポンスモデル", type = "object")
  public class MeResponse {

      /** ユーザ名 */
+     @Schema(description = "ユーザ名", type = "string", example = "nob")
      private String name;

      /** 年齢 */
+     @Schema(description = "年齢", type = "integer", example = "13")
      private Integer age;
  }

SampleExceptionHandler.java

例外発生時レスポンスモデルのスキーマ定義を記載します:

  package nob.example.easyapp.handler;

  import org.springframework.http.HttpStatus;
  import org.springframework.http.ResponseEntity;
  import org.springframework.web.bind.annotation.ExceptionHandler;
  import org.springframework.web.bind.annotation.RestControllerAdvice;

+ import io.swagger.v3.oas.annotations.media.Schema;
  import lombok.Value;
  import nob.example.easyapp.exception.SampleException;

  /**
   * サンプル例外のハンドラです。
   *
   * @author nob
   */
  @RestControllerAdvice
  public class SampleExceptionHandler {

      /**
       * サンプル例外が投げられた際のハンドリングを行います。
       *
       * @param e
       * @return 例外メッセージ
       */
      @SuppressWarnings({ "unchecked", "rawtypes" })
      @ExceptionHandler(SampleException.class) // SampleExceptionが投げられた際に動く
      public ResponseEntity<SampleExceptionResponseBody> handleSampleException(SampleException e) {

          return new ResponseEntity(new SampleExceptionResponseBody(e.getMessage()), HttpStatus.UNPROCESSABLE_ENTITY);
      }

      /**
       * サンプル例外発生時のレスポンスボディです。
       */
      @Value
+     @Schema(description = "サンプルエラーのレスポンス", type = "object")
      public class SampleExceptionResponseBody {

          /** エラーメッセージ */
+         @Schema(description = "エラーメッセージ", type = "string", example = "業務エラーが発生しました。")
          private String message;
      }
  }

resources/application-swagger.yaml

API の description について記載します:

########################
### Easy App documents
########################
easyappdoc:
  describe:
    api:
      v1:
        login: |
          認証処理を行います。リクエストに不備があった場合はエラーレスポンスを返します。
        me: |
          ユーザ情報を取得します。

動作確認

アプリ起動後、http://localhost:8080/swagger-ui/index.html で swagger ドキュメントを確認できます。

Tips

Try it out ボタンを無効化したい場合

application.properties に下記を追加すればボタンが非表示になります:

springdoc.swagger-ui.supported-submit-methods=[]