1. 序章

Swagger仕様は、OpenAPI仕様としてよく知られています。 Smartbear は、JavaおよびKotlinコード用の Swaggerアノテーションなどの一部のツールを引き続きサポートしますが、可能な限り、すべてのOpenAPIバージョンを使用することをお勧めします。

ここでは、プロジェクトにおけるOpenAPI仕様の利点に触れます。 主なアプローチは2つあります。コードがあり、その仕様を生成するか、仕様があり、コードを生成するかです。

いずれの場合も、OpenApi/Swaggerツールは自動APIドキュメントページを提供します。

2. SpringBootプロジェクトへのOpenAPIの紹介

Gradleを使用してプロジェクトを設定しましょう。 Spring Initializr で、Gradle、Kotlin、およびJDK17を使用してプロジェクトを作成します。 SpringWebフレームワークが唯一の依存関係になります。 次に、実行の準備ができているプロジェクトをダウンロードできます。

To プロジェクトでドキュメントの表示を開始します3つの依存関係を追加するだけで十分です

implementation("org.springdoc:springdoc-openapi-data-rest:1.6.0")
implementation("org.springdoc:springdoc-openapi-ui:1.6.0")
implementation("org.springdoc:springdoc-openapi-kotlin:1.6.0")

それでおしまい。 これで、プロジェクトを実行して http:// localhost:8080 /swagger-ui.htmlにアクセスできます。 空のドキュメントページが表示されます。 それを入力しましょう。

3. 既存のコードから仕様を生成する

多くの場合、私たちはすでにいくつかの歴史を持っているプロジェクトに行きます。 このようなプロジェクトはAPIを公開しますが、新しいクライアントをそのAPIにオンボーディングすることは、通常、困難を伴います。 いつものように、すべてが変更され、ドキュメントは公開された日に廃止されます。

ドキュメントの信頼できる情報源としてコードを使用することにより、ドキュメントの関連性が常に維持されます。 これは、組織外のユーザーにAPIを提供する場合に特に意味があります。 したがって、誰にも相談せずに新しいバージョンを作成できます。 次に、新しいAPIセットがオンラインになったときにクライアントに通知します。

3.1. 注釈を使用して仕様を生成する

生成されたドキュメントの目標を達成するには、コードに注釈を付ける必要があります。これは、RESTコントローラーと、引数および戻り値として機能するモデルの両方です。 モデルについては、@Schemaアノテーションを確認する必要があります。 これは、データ転送クラスの主な注釈です。

@Schema(description = "Model for a dealer's view of a car.")
data class DealerCar(
  // Other fields
  @field:Schema(
    description = "A year when this car was made",
    example = "2021",
    type = "int",
    minimum = "1900",
    maximum = "2500"
  )
  val year: Int,
  // More fields
)

このアノテーションには多くの属性があります。値の範囲を指定したり、値の例を提供したり、文字列フィールドの形式を指定したりします。 swagger-core ライブラリがそれらを正しく処理できるように、クラスフィールドでKotlin固有のfieldターゲットを使用する必要があります。

RESTコントローラーのメソッドには、その目的と使用法を説明する@Operationアノテーションがあります。 また、返品コードを指定して、返品する場合を説明することもできます。

    @Operation(summary = "Sets a price for a chosen car", description = "Returns 202 if successful")
    @ApiResponses(
      value = [
          ApiResponse(responseCode = "202", description = "Successful Operation"),
          ApiResponse(responseCode = "404", description = "Such a car does not exist"),
      ]
    )
    @PostMapping("/price/{carId}", consumes = ["application/json"])
    fun setPrice(
      @PathVariable carId: Int,
      @RequestBody @OASRequestBody(
        description = "New price for the car",
        content = [Content(
          mediaType = "application/json",
          schema = Schema(type = "number", format = "float", minimum = "0.0", example = "23000.34"),
        )]
      ) price: BigDecimal
    ): ResponseEntity<Unit> {
        carService.setPrice(carId, price)
        return ResponseEntity.accepted().build()
    }

ここで、メソッドが返す可能性のあるすべての応答と、それが取るリクエスト本文を引数として指定できます。 言うまでもなく、このアプローチはすぐに管理できなくなり、アノテーションは実際のコードよりもはるかに多くのスペースを占めるため、仕様から始めることで利益が得られることがよくあります。

4. 既存の仕様からのサーバーまたはクライアントの生成

クライアントの仕様に準拠することとHTTPWebポイントを作成することの両方は、多くの場合、反復的で面倒です。 そのコードを生成することで、私たちは私たちの生活を楽にします。

最初に仕様を設定するもう1つの用途は、フロントエンドとバックエンドの間で仕様を合意する方が簡単であるということです。

そして、すでに見たように、仕様とコードを分離することで、より読みやすいソースを実現できます。

4.2. 仕様ファイルを使用したSpringServerの作成

まず、仕様ファイルを作成する必要があります。 そのような例は、 Swagger OnlineEditorサイトにあります。 ただし、簡単なデモンストレーションでは、GitHubにあるより簡単なセットアップを使用します。

次に、 OpenAPIGeneratorツールを使用します。 スタンドアロンのCLIツールとして利用できるため、プロジェクトを開始するために1回実行することになっています。 ただし、仕様が変更されるたびにが実行されると、代わりには、仕様への準拠に関する一定のフィードバックループを提供します。 したがって、それは最大の利点をもたらします。

そこで、プロジェクトbuild.gradle.ktsに追加します。

// The values oasPackage, oasSpecLocation, oasGenOutputDir are defined earlier
tasks.register("generateServer", org.openapitools.generator.gradle.plugin.tasks.GenerateTask::class) {
    input = project.file(oasSpecLocation).path
    outputDir.set(oasGenOutputDir.get().toString())
    modelPackage.set("$oasPackage.model")
    apiPackage.set("$oasPackage.api")
    packageName.set(oasPackage)
    generatorName.set("kotlin-spring")
    configOptions.set(
      mapOf(
        "dateLibrary" to "java8",
        "interfaceOnly" to "true",
        "useTags" to "true"
      )
    )
}

このタスクへの依存関係をKotlinCompileタスクに追加し、生成されたソースをメインソースセットに追加することを覚えておく必要があります。 このプラグインはRESTサーバー全体を生成できるため、ApiDelegateに実際のビジネスロジックを提供する必要があります。 しかし、私たちの目的では、インターフェースだけで十分です。

class CarCrudController(private val carService: CarService) : CarsApi {
    override fun createCar(carBody: CarBody): ResponseEntity<Car> = TODO("Implementation")
    override fun getCar(id: Int): ResponseEntity<Car> = TODO("Implementation")
    override fun getCars(): ResponseEntity<List<Car>> = TODO("Implementation")
}

4.3. 既存の仕様からのクライアントの生成

サーバー生成と同様に、クライアントも作成できます。 Gradleスクリプトの1行を変更し、 generatorName.set( “kotlin”)を使用する必要があります。 その後、クライアントに必要な依存関係を追加する必要があります。

implementation("com.squareup.moshi:moshi-kotlin:1.13.0")
implementation("com.squareup.moshi:moshi-adapters:1.13.0")
implementation("com.squareup.okhttp3:okhttp:4.9.3")

次に、クライアントを呼び出すことができます。

val car = CarsApi("http://localhost:8080")
  .createCar(ClientCarBody(model = "CM-X", make = "Gokyoro", year = 2021))

5. 結論

このチュートリアルでは、Swaggerの後継であるOpenAPIを使用して、APIを実装するソースコードがある場合に API仕様を作成し、仕様からコードを作成する、および同じ仕様から生成されたユーザーフレンドリーなドキュメントを表示します。

いつものように、サンプルのコードはGitHubにあります。