Kotlin製Webアプリケーションフレームワーク Ktor の使い方1 〜プロジェクト作成から Hello World 〜

f:id:Naotsugu:20201208213355p:plain


Ktor とは

Ktor は、Kotlin の Web アプリケーションフレームワークです。

Kotlin の本山である JetBrains 社により開発が続けられており、2018年末の v1.0 リリースからアップデートが続き、現在は v.1.4.3 がリリースされています。

軽量であり、高い拡張性を持ち、マルチプラットフォームであり、そして Kotlin の coroutines による非同期処理により高いスケーラビリティを発揮します。

ここでは、Ktor による簡単な Web アプリケーションの作成を、何回かに分けて見ていきたいと思います。


プロジェクト作成

Gradle によりプロジェクトを作成します。

gradle init タスクにてプロジェクトを生成しましょう。

$ mkdir ktor-example
$ cd ktor-example
$ gradle init

gradle init タスクでは以下のようにオプション選択しました。

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 4

Split functionality across multiple subprojects?:
  1: no - only one application project
  2: yes - application and library projects
Enter selection (default: no - only one application project) [1..2] 1

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Kotlin) [1..2] 2

Project name (default: ktor-example):
Source package (default: ktor.example):


プロジェクトが作成されたら build.gradle.kts を以下のように変更します。

plugins {
    id("org.jetbrains.kotlin.jvm") version "1.4.20"
    application
}

repositories {
    jcenter()
}

dependencies {
    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
    implementation("org.jetbrains.kotlin:kotlin-stdlib")
    implementation("io.ktor:ktor-server-netty:1.4.3")
    implementation("ch.qos.logback:logback-classic:1.2.3")
    testImplementation("org.jetbrains.kotlin:kotlin-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}

application {
    mainClass.set("ktor.example.AppKt")
}

Ktor では、Netty であったり、Jetty や Tomcat などのサーブレットコンテナを使うことができます。

ここでは、Netty を使うため ktor-server-netty の依存を追加しました。 それ以外は Gradle の Kotlin DSL の標準的な内容です。


Hello World

App.kt を以下のように編集します。

package ktor.example

import io.ktor.application.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*

fun main(args: Array<String>) {
    val server = embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                call.respondText("Hello World!", ContentType.Text.Plain)
            }
            get("/demo") {
                call.respondText("HELLO WORLD!")
            }
        }
    }
    server.start(wait = true)
}

embeddedServer として Netty を選択し、routing にて2つのパスを登録しています。

respondText() により固定の文字列をレスポンスするだけの内容です。

実行してみましょう。

$ ./gradlew run

起動したら http://localhost:8080 にアクセスします。

f:id:Naotsugu:20201208215903p:plain

もう一つのパス http://localhost:8080/demo でアクセスします。

f:id:Naotsugu:20201208220014p:plain

簡単ですね。


Application オブジェクト

先程の例は簡単でしたが、アプリケーションの構成などが増えた場合には、Ktor により提供される Application オブジェクトを利用すると便利です。

Application オブジェクトは、アプリケーションの構成を定義することで、リクエストを処理できるWebアプリケーションとなります。

Application オブジェクトの拡張関数 module() を以下のように定義します。

fun Application.module() {
    install(DefaultHeaders)
    install(CallLogging)
    install(Routing) {
        get("/") {
            call.respondText("Hello World!", ContentType.Text.Html)
        }
        get("/demo") {
            call.respondText("HELLO WORLD!")
        }
    }
}

install() により必要な機能を構成します。 Routing によりリクエストに応じたルーティングを構成します。

main 関数は以下のようになります。

fun main(args: Array<String>) {
    embeddedServer(Netty, 8080,
            watchPaths = listOf("AppKt"),
            module = Application::module)
            .start()
}

watchPaths には自動リロードの監視対象のパス文字列を指定します。

module には先程作成した Application オブジェクトの拡張関数 を指定します。

以下のように実行します。

$ ./gradlew run

アプリケーションが起動し、先程と同様の結果が得られます。

なお、Routing は以下のように外だしして定義することもできます。

fun Application.module() {
    routing {
        root()
    }
}

fun Routing.root() {
    get("/") {
        call.respondText("Hello World!")
    }
}


アプリケーション設定値の抽出

先程の例では、ポート番号といったアプリケーションの設定を、ソースコードに直接記載していました。

これらの設定を外部ファイルに抜き出しましょう。

app/src/main/resources/application.conf として設定ファイルを新規作成します。

ktor {
    deployment {
        port = 8080
        port = ${?PORT}
    }

    application {
        modules = [ ktor.example.AppKt.main ]
    }
}

ポート番号と、アプリケーションモジュールを外部ファイルに抜き出しました。

App.kt を以下のように編集します。

package ktor.example

import io.ktor.application.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.features.*

fun Application.main() {
    install(DefaultHeaders)
    install(CallLogging)
    install(Routing) {
        get("/") {
            call.respondText("Hello World!", ContentType.Text.Html)
        }
        get("/demo") {
            call.respondText("HELLO WORLD!")
        }
    }
}

拡張関数は main() に変更し、もともとあった main() 関数を削除しました。

build.gradle.kts で アプリケーションプラグインで指定するメインクラスを、Ktor の io.ktor.server.netty.EngineMain に変更します。

application {
    mainClass.set("io.ktor.server.netty.EngineMain")
}

これにより application.conf の設定によりアプリケーションが起動します。

その他、以下のエンジンが利用できます。

  • io.ktor.server.cio.EngineMain
  • io.ktor.server.tomcat.EngineMain
  • io.ktor.server.jetty.EngineMain
  • io.ktor.server.netty.EngineMain


application.confktor.deployment.watch を設定することで、対象パスを監視し、自動リロードが有効になります。

今回の例ではモジュールのパスが app となるため以下のように設定します。

ktor {
    deployment {
        watch = [ app ]
    }
}

intellij idea であれば、ソースを更新後、Build -> Build Projects とすれば更新が反映されます。


次回に続きます。

blog1.mammb.com



Kotlinイン・アクション

Kotlinイン・アクション

Kotlinプログラミング

Kotlinプログラミング