昨日、KtorのLocationsの使い方という記事を投稿したのですが、Ktor
のタグがついた記事が無かったのでHello world的なものを投稿しておきます。
Kotlin製のWeb frameworkであるKtor
のプロジェクトの作成からJSONを返すところまでを書こうと思います。
だいたい公式に書いてありますが。
Kotlin
のVersionは1.1.51
、Ktor
のVersionは0.9.0
です。
2017/11/29追記
Kotlin
のVersionを1.2.0
にあげても普通に動きました。
ビルドツールはGradle
、IDEはIntelliJ IDEA
を使います。
Hello world
目次
- プロジェクトの作成
-
build.gradle
の追記 -
Application.kt
の作成と編集 - 設定ファイルの作成と編集
- Run
1. プロジェクトの作成
IntelliJ IDEAで create new projectからGradle
を選択し、Java
とKotlin(Java)
にチェックを入れてプロジェクト作成まで進めます。
あとは適切な名前をつけてUse auto-importにチェックを入れるぐらいです。
2. build.gradle
の追記
生成されたプロジェクトのbuild.gradle
を編集していきます。
初期状態はこんな感じだと思います。
group 'Example'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.1.51'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
やることとしては
-
Ktor
関連に必要なrepository
とdependency
を追加 -
application
pluginの追加
です。
2-1 Ktor
関連に必要なrepository
とdependency
を追加
まずKtor
のバージョンを定義しておきましょう。
buildscript {
ext {
kotlin_version = '1.1.51'
ktor_version = '0.9.0'
}
...
}
次に必要なrepository
を追加します。
Ktor
とKtor
が依存しているKotlin
のcoroutine
関連はbintray上にあるのでそれらを追加します。
repositories {
mavenCentral()
// 以下2つを追加👇
maven { url "http://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}
これでdependency
を追加する準備が整いました。
Ktor
とNetty
、ログ出力のためにlogback
を追加します。
あとで使うのでJSONパーサーのjackson
も追加しておきます。
jackson
については、Ktor
のFeature
として提供されているものを使用します。
これにはjackson-module-kotlin
が同梱されています。
dependencies {
// Kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
// Ktor 以下3つを追加👇
compile "io.ktor:ktor-server-core:$ktor_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-jackson:$ktor_version"
// Log 追加👇
compile "ch.qos.logback:logback-classic:1.2.1"
// Testing
testCompile group: 'junit', name: 'junit', version: '4.12'
}
Kotlin
のcoroutine
はまだexperimental(実験的な)機能のため、利用可能にするために設定が必要ですので以下のコードを追加しましょう。
kotlin {
experimental {
coroutines "enable"
}
}
2-2 application
pluginの追加
2-1だけでもKtor
を動かすことはできますが、application
pluginの追加もしてしまいましょう。
apply plugin: 'java'
apply plugin: 'kotlin'
// 追加👇
apply plugin: 'application'
併せてmainClassName
を指定します。この時に、io.ktor.server.netty.DevelopmentEngine
を指定します。
mainClassName = 'io.ktor.server.netty.DevelopmentEngine'
これでbuild.gradle
の編集は終了です。
最終的にはこんな感じになります。
group 'Example'
version '1.0-SNAPSHOT'
buildscript {
ext {
kotlin_version = '1.1.51'
ktor_version = '0.9.0'
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'
sourceCompatibility = 1.8
mainClassName = 'io.ktor.server.netty.DevelopmentEngine'
repositories {
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}
dependencies {
// Kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
// Ktor
compile "io.ktor:ktor-server-core:$ktor_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-jackson:$ktor_version"
// Log
compile "ch.qos.logback:logback-classic:1.2.1"
// Testing
testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
kotlin {
experimental {
coroutines "enable"
}
}
3. Application.kt
の作成と編集
main
関数を記述するファイルを作成します。
package example
import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.application.install
import io.ktor.features.CallLogging
import io.ktor.response.respond
import io.ktor.routing.get
// 1. io.ktor.application.Applicationの拡張関数としてmain()を定義
fun Application.main() {
// 2. Loggingの機能をinstall
install(CallLogging)
// 3. Routingの設定
install(Routing) {
get("/hello") {
call.respond("Hello world from Ktor!")
}
}
}
-
io.ktor.application.Applicationの拡張関数としてmain()を定義
fun main(args: Array<String>) {}
ではなく、Application.main() {}
で定義し、ここにアプリケーションの設定などを書いていきます。 -
Loggingの機能をinstall
build.gradle
の追記でlogback
追加しましたがそれを利用するためにCallLogging feature
をinstallします。
Ktor
ではこのように必要な機能をFeature
という形で提供しており、それぞれinstallして利用します。 -
Routingの設定
Routing
もFeature
ですので、installします。そして、その中にルーティングと処理を記載します。
今回は/hello
にアクセスするとHello world from Ktor!
という文字列を返すように記述しています。
Application.kt
に関してはこれで終わりです。
4. 設定ファイルの作成と編集
アプリケーションの設定ファイルapplication.conf
とlogback
の設定ファイルlogback.xml
を作成します。
どちらも/src/main/resources
直下に配置します。
ktor {
deployment {
port = 8080
}
application {
modules = [ example.ApplicationKt.main ]
}
}
application.conf
はPlay frameworkでも採用されているTypesafe Config
を使用して記述します。
deployment.port
で起動するアプリケーションのポートを、application.modules
でmain
関数を指定します。
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>
公式のものをコピペしました。お好みにカスタマイズしてください。
4. Run
ようやくHello worldする準備が整いました。
$ gradle run
やIntelliJ IDEAにRun/Debug configurationを追加して実行ができます。
Run/Debug configurationはMain class
とUse classpath of module
を設定するだけです。
Use classpath of module
は作成したプロジェクト名のものに読み替えてください。
http://localhost:8080
にアクセスして、Hello worldしましょう。
JSONを返す
Hello worldだけしてもあれなので、JSONを返すようにします。
目次
-
Jackson feature
をinstall - data classを返すエンドポイントを追加
- Run
-
Jackson Datatype
の追加例
1. Jackson feature
をinstall
上記でjackson
をdependency
に追加しているので、Application.main()
でinstallするだけで利用可能になります。
// 追加するimportだけ記載しています
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.features.ContentNegotiation
import io.ktor.jackson.jackson
fun Application.main() {
// ...
install(ContentNegotiation) {
jackson {
configure(SerializationFeature.INDENT_OUTPUT, true)
}
}
install(Routing) {
// ...
}
}
install(ContentNegotiation)
の中でjackson
の設定を行います。
出力されるJSONがインデントされた状態になるようにだけ設定をしています。
2. data class
を返すエンドポイントを追加
続いてエンドポイントを追加します。ここでは/item
とします。
返すのはなんでもいいのですが、お手軽なので簡単なItem
というdata class
を定義して返すようにします。
fun Application.main() {
// ...
install(Routing) {
// ...
get("/item") {
call.respond(Item(1, "Hoge"))
}
}
}
data class Item(val id: Int, val name: String)
実装はこれだけです。
3. Run
アプリケーションを起動して、http://localhost:8080/item
にアクセスすると以下のJSONが返却されると思います。
{
"id": 1,
"name": "Hoge"
}
4. Jackson Datatype
の追加例
Jacksonには非標準の型のSerializer/Deserializerに対応するために追加モジュールが用意されています。
今回はJSR-310日付型に対応するjackson-datatype-jsr310
モジュールを追加してLocalDate
型のデフォルトSerializer/Deserializerを設定してみます。
-
dependency
の追加 -
Application.kt
の編集 - Run
1. dependency
の追加
依存関係をbuild.gradle
に追加します。
dependencies {
// ...
compile ('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.2')
// ...
}
-
Application.kt
の編集
jackson
の設定と、Item
クラスにLocalDate
型のフィールドを追加します。
ここでは、デフォルトで"yyyy/MM/dd"
形式でSerializer/Deserializerされるように設定しています。
スコープ関数のapply
でスッキリ書けますね。
// 追加するimportだけ記載しています
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer
import java.time.LocalDate
import java.time.format.DateTimeFormatter
fun Application.main() {
// ...
install(ContentNegotiation) {
jackson {
configure(SerializationFeature.INDENT_OUTPUT, true)
registerModule(JavaTimeModule().apply {
addSerializer(LocalDate::class.java, LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy/MM/dd")))
addDeserializer(LocalDate::class.java, LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy/MM/dd")))
})
}
}
install(Routing) {
// ...
get("/item") {
call.respond(Item(1, "Hoge", LocalDate.now()))
}
}
}
data class Item(val id: Int, val name: String, val date: LocalDate)
3. Run
アプリケーションを起動して、http://localhost:8080/item
にアクセスすると以下のJSONが返却されると思います。
{
"id": 1,
"name": "Hoge",
"date": "2017/11/24"
}
もっと簡単にHello worldする方法もあるけど、外だしできる設定は外出しした方がいい気がするので、現状これでいいんじゃないかな。