LoginSignup
41
30

More than 5 years have passed since last update.

KtorでJSONを返すまで

Last updated at Posted at 2017-11-24

昨日、KtorのLocationsの使い方という記事を投稿したのですが、Ktorのタグがついた記事が無かったのでHello world的なものを投稿しておきます。

Kotlin製のWeb frameworkであるKtorのプロジェクトの作成からJSONを返すところまでを書こうと思います。
だいたい公式に書いてありますが。
KotlinのVersionは1.1.51KtorのVersionは0.9.0です。

2017/11/29追記
KotlinのVersionを1.2.0にあげても普通に動きました。

ビルドツールはGradle、IDEはIntelliJ IDEAを使います。

Hello world

目次

  1. プロジェクトの作成
  2. build.gradleの追記
  3. Application.ktの作成と編集
  4. 設定ファイルの作成と編集
  5. Run

1. プロジェクトの作成

IntelliJ IDEAで create new projectからGradleを選択し、JavaKotlin(Java)にチェックを入れてプロジェクト作成まで進めます。
あとは適切な名前をつけてUse auto-importにチェックを入れるぐらいです。

2. build.gradleの追記

生成されたプロジェクトの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'
}

やることとしては

  1. Ktor関連に必要なrepositorydependencyを追加
  2. application pluginの追加

です。

2-1 Ktor関連に必要なrepositorydependencyを追加

まずKtorのバージョンを定義しておきましょう。

build.gradle
buildscript {
    ext {
        kotlin_version = '1.1.51'
        ktor_version = '0.9.0'
    }
    ...
}

次に必要なrepositoryを追加します。
KtorKtorが依存しているKotlincoroutine関連はbintray上にあるのでそれらを追加します。

build.gradle
repositories {
    mavenCentral()
    // 以下2つを追加👇
    maven { url  "http://dl.bintray.com/kotlin/ktor" }
    maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}

これでdependencyを追加する準備が整いました。
KtorNetty、ログ出力のためにlogbackを追加します。
あとで使うのでJSONパーサーのjacksonも追加しておきます。
jacksonについては、KtorFeatureとして提供されているものを使用します。
これにはjackson-module-kotlinが同梱されています。

build.gradle
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'
}

Kotlincoroutineはまだexperimental(実験的な)機能のため、利用可能にするために設定が必要ですので以下のコードを追加しましょう。

build.gradle
kotlin {
    experimental {
        coroutines "enable"
    }
}

2-2 application pluginの追加

2-1だけでもKtorを動かすことはできますが、applicationpluginの追加もしてしまいましょう。

build.gradle
apply plugin: 'java'
apply plugin: 'kotlin'
 // 追加👇
apply plugin: 'application'

併せてmainClassNameを指定します。この時に、io.ktor.server.netty.DevelopmentEngineを指定します。

build.gradle
mainClassName = 'io.ktor.server.netty.DevelopmentEngine'

これでbuild.gradleの編集は終了です。

最終的にはこんな感じになります。

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関数を記述するファイルを作成します。

Application.kt
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!")
        }
    }
}
  1. io.ktor.application.Applicationの拡張関数としてmain()を定義
    fun main(args: Array<String>) {}ではなく、Application.main() {}で定義し、ここにアプリケーションの設定などを書いていきます。

  2. Loggingの機能をinstall
    build.gradleの追記logback追加しましたがそれを利用するためにCallLogging featureをinstallします。
    Ktorではこのように必要な機能をFeatureという形で提供しており、それぞれinstallして利用します。

  3. Routingの設定
    RoutingFeatureですので、installします。そして、その中にルーティングと処理を記載します。
    今回は/helloにアクセスするとHello world from Ktor!という文字列を返すように記述しています。

Application.ktに関してはこれで終わりです。

4. 設定ファイルの作成と編集

アプリケーションの設定ファイルapplication.conflogbackの設定ファイルlogback.xmlを作成します。
どちらも/src/main/resources直下に配置します。

application.conf
ktor {
  deployment {
    port = 8080
  }
  application {
    modules = [ example.ApplicationKt.main ]
  }
}

application.confはPlay frameworkでも採用されているTypesafe Configを使用して記述します。
deployment.portで起動するアプリケーションのポートを、application.modulesmain関数を指定します。

logback.xml
<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 classUse classpath of moduleを設定するだけです。
Use classpath of moduleは作成したプロジェクト名のものに読み替えてください。
スクリーンショット 2017-11-24 15.41.07.png

http://localhost:8080にアクセスして、Hello worldしましょう。

JSONを返す

Hello worldだけしてもあれなので、JSONを返すようにします。

目次

  1. Jackson featureをinstall
  2. data classを返すエンドポイントを追加
  3. Run
  4. Jackson Datatypeの追加例

1. Jackson featureをinstall

上記jacksondependencyに追加しているので、Application.main()でinstallするだけで利用可能になります。

Application.kt

// 追加する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を定義して返すようにします。

Application.kt
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を設定してみます。

  1. dependencyの追加
  2. Application.ktの編集
  3. Run

1. dependencyの追加

依存関係をbuild.gradleに追加します。

build.gradle
dependencies {
    // ...

    compile ('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.2')

    // ...
}
  1. Application.ktの編集

jacksonの設定と、ItemクラスにLocalDate型のフィールドを追加します。
ここでは、デフォルトで"yyyy/MM/dd"形式でSerializer/Deserializerされるように設定しています。
スコープ関数のapplyでスッキリ書けますね。

Application.kt

// 追加する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する方法もあるけど、外だしできる設定は外出しした方がいい気がするので、現状これでいいんじゃないかな。

41
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
30