blog

KtorでWebプロジェクトを迅速に開発する方法

I. Ktor はじめに Ktor は Kotlin ベースの高性能な Web 開発フレームワークで、Kotlin や DSL などの機能をサポートしています。 Ktor のサーバーサイドは JVM ...

Jun 28, 2020 · 8 min. read
シェア

. Ktor

Ktorは、Kotlinチームによって構築されたWebフレームワークで、非同期、高性能、軽量のWebサーバーを作成し、Kotlinの通常のAPIを使用して、ノンブロッキング、マルチプラットフォームのWebクライアントを構築します。

Ktorのサーバー側はJVMに限定されていますが、Ktorのクライアント側はマルチプラットフォームライブラリです。

Ktor のクライアントを Http フレームワークとして使用することは、Kotlin マルチプラットフォームでクロスプラットフォームプロジェクトを構築する際の良い選択です。

Ktorはサーバーエンジンと柔軟な非同期HTTPクライアントの2つの部分から構成されています。現在のリリースでは、HTTPクライアントに焦点を当てています。クライアントはJVM、JS、Android、iOSをサポートするマルチプラットフォームライブラリで、現在ではクロスプラットフォームのモバイルアプリケーションでよく使用されています。

. Ktor サーバーサイドでの使用

Ktorのサーバーサイド・アプリケーションは様々な方法で実行することができます:

  • main() で embeddedServer を呼び出し、Ktor アプリケーションを起動します。
  • HOCONのapplication.conf設定ファイルを使用してEngineMain main()を実行します。
  • ウェブサーバーとしてのサーブレット
  • withTestApplication を使用して、Ktor アプリケーションをテスト内で起動します。

Gradle Ktorの設定

KtorはKotlinのコルーチンに依存しているため、Kotlinのバージョン1.3.xが必要です。

Ktorを使用する必要があるモジュールに、以下の依存関係を追加します:

dependencies {
 ...
 implementation "io.ktor:ktor-server-core:${libs.ktor}"
 implementation "io.ktor:ktor-server-netty:${libs.ktor}"
}

この後の例では、freemarkerやgsonなど、Ktorの他のアーティファクトを紹介します。

embeddedServer

例としてNettyをサーバーエンジンとして使用し、embeddedServer経由でKtorアプリケーションを起動します:

fun main() {
 embeddedServer(Netty, port?:8080, watchPaths = listOf("MainKt"), module = Application::module).start()
}

ApplicationCall && Routing

Ktor のパイプラインは、ルーティング、圧縮などの特定の機能を提供する、1つ以上のプリインストールされたインターセプターで構成され、最終的にリクエストを処理します。

Ktorのルーティングは、DSLを使ったコンフィギュレーションだけでなく、Restfulなアプローチもサポートしています。

ルーティングはルーティングツリーと呼ばれるネストをサポートし、複雑なルールのマッチングやリクエストの再帰的な処理を可能にします。

CORS

デフォルトでは、Ktorはインターセプターを提供し、クロスドメインリソース共有の適切なサポートを可能にします。

まず、アプリケーションにCORS機能をインストールします。

fun Application.main() {
 ...
 install(CORS)
 ...
}

Ktor CORS 機能のデフォルト設定では、GET、POST、HEAD の HTTP メソッドと以下のヘッダーのみを扱います:

 HttpHeaders.Accept
 HttpHeaders.AcceptLanguages
 HttpHeaders.ContentLanguage
 HttpHeaders.ContentType

次の例は、CORS 機能を構成する方法を示しています。

fun Application.main() {
 ...
 install(CORS)
 {
 method(HttpMethod.Options)
 header(HttpHeaders.XForwardedProto)
 anyHost()
 host("my-host")
 // host("my-host:80")
 // host("my-host", subDomains = listOf("www"))
 // host("my-host", schemes = listOf("http", "https"))
 allowCredentials = true
 allowNonSimpleContentTypes = true
 maxAge = Duration.ofDays(1)
 }
 ...
}

Packing

Ktor アプリケーションをデプロイする場合、fat jar または war パッケージを使用できます。

例えばfat jarの場合、Ktorアプリケーションはgradleのshadowプラグインを使って簡単にパッケージ化できます。

プロジェクトのルートディレクトリにあるbuild.gradleにシャドウプラグインの依存関係を追加します:

buildscript {
 repositories {
 jcenter()
 mavenCentral()
 }
 dependencies {
 classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
 ......
 }
}

次に、パッケージ化したいモジュールに shadow プラグインを追加し、jar パッケージ名と jar パッケージのエントリ Main 関数を出力します:

plugins {
 id 'java'
 id 'kotlin'
 id 'com.github.johnrengelman.shadow'
}
......
shadowJar {
 baseName = 'xxx' // jar  
 manifest {
 attributes["Main-Class"] = "xxx.xxx.xxx.xxx" // jar パッケージの主な機能
 }
}

.

この記事では、 RxCache 例に、Ktorを使用してディスク・キャッシュからデータを読み込むローカル・キャッシュ・ブラウザの開発について説明します。

RxCacheはJavaとAndroid用のローカルキャッシュです。現在、インメモリ、オフヒープメモリ、ディスクキャッシュをサポートしています。

開発の背景:Ubuntuにデプロイされているデスクトップ・アプリケーションの中には、データを埋め込む必要があるものがいくつかあり、RxCache自体がディスク上のキャッシュをサポートしています。そのため、RxCacheを使って埋めたデータを保存し、ローカルに埋めたデータを表示するブラウザ・アプリケーションが必要です。

RxCache

RxCache はシングルトンなので、まず config() を呼び出して RxCache を構成する必要があります。

RxCache は、メモリ、永続性、およびさまざまなシリアライズ方法という、2 番目のレベルのキャッシュをサポートしています。これらは設定によって反映させることができます。

val rxCache: RxCache by lazy {
 val converter: Converter = when (Config.converter) {
 "gson" -> GsonConverter() 
 "fastjson" -> FastJSONConverter()
 "moshi" -> MoshiConverter()
 "kryo" -> KryoConverter()
 "hessian" -> HessianConverter()
 "fst" -> FSTConverter()
 "protobuf" -> ProtobufConverter()
 else -> GsonConverter()
 }
 RxCache.config {
 RxCache.Builder().persistence {
 when (Config.type) {
 "disk" -> {
 val cacheDirectory = File(Config.path) // rxCache 永続層ストレージ・アドレス
 if (!cacheDirectory.exists()) {
 cacheDirectory.mkdir()
 }
 DiskImpl(cacheDirectory, converter)
 }
 "okio" -> {
 val cacheDirectory = File(Config.path) // rxCache 永続層ストレージ・アドレス
 if (!cacheDirectory.exists()) {
 cacheDirectory.mkdir()
 }
 OkioImpl(cacheDirectory, converter)
 }
 "mapdb" -> {
 val cacheDirectory = File(Config.path) // rxCache 永続層ストレージ・アドレス
 MapDBImpl(cacheDirectory, converter)
 }
 "diskmap"-> {
 val cacheDirectory = File(Config.path) // rxCache 永続層ストレージ・アドレス
 DiskMapImpl(cacheDirectory, converter)
 }
 else -> {
 val cacheDirectory = File(Config.path) // rxCache 永続層ストレージ・アドレス
 if (!cacheDirectory.exists()) {
 cacheDirectory.mkdir()
 }
 DiskImpl(cacheDirectory, converter)
 }
 }
 }
 }
 RxCache.getRxCache()
}

module

Ktor モジュールは、Application クラスを受け取る開発者定義関数です。

この例では、DefaultHeaders、CallLogging、FreeMarker、ContentNegotiation、Routingがインストールされています。

fun Application.module() {
 install(DefaultHeaders)
 install(CallLogging)
 install(FreeMarker) {
 templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates")
 defaultEncoding = "utf-8"
 }
 install(ContentNegotiation) {
 gson {
 setDateFormat(DateFormat.LONG)
 setPrettyPrinting()
 }
 }
 install(Routing) {
 ......
 }
}

Routing

ルーティングは外部ページを提供します。

 install(Routing) {
 static("/") {
 defaultResource("index.html", "web")
 }
 post("/saveConfig") {
 val postParameters: Parameters = call.receiveParameters()
 Config.path = postParameters["path"] ?: ""
 Config.type = postParameters["type"] ?: ""
 Config.converter = postParameters["converter"] ?: ""
 call.respond(FreeMarkerContent("save.ftl", mapOf("config" to Config)))
 }
 get("/list") {
 val file = File(Config.path)
 val array = file.list()
 call.respond(array)
 }
 get("/detail/{key}") {
 val key = call.parameters["key"]
 val json = rxCache.getStringData(key)
 call.respondText(json)
 }
 get("/info") {
 val json = rxCache.info
 call.respondText(json)
 }
 }

index.htmlはRxCacheの設定に使用されます。

<html>
<h2>Hi</h2>
RxCache's path: ${config.path} </br>
RxCache's persistence: ${config.type} </br>
RxCache's serialization: ${config.converter} </br>
</html>

リスト・インターフェースと詳細インターフェースは、ディスクに保存されているデータのキーを表示し、キーに基づいてストレージの詳細を照会するために使用されます。

info インタフェースは、キャッシュ内の情報を表示するために使用されます。

スタートアップ

ブラウザはコマンドラインからの引数を解析する kotlinx-cliで設定します。現在のところ、Ktor アプリケーションを起動するポート番号を示す '-p' のみがサポートされています。

ブラウザはサーバーエンジンとしてNettyを使用します。

fun main(args: Array<String>) {
 val parser = ArgParser("rxcache-browser")
 val port by parser.option(ArgType.Int, shortName = "p", description = "Port number of the local web service")
 parser.parse(args)
 embeddedServer(Netty, port?:8080, watchPaths = listOf("MainKt"), module = Application::module).start()
}

.

Ktorは最小限のコードと設定でアプリケーションを構築するので、とても簡単です。

シンプルなWebプロジェクトや、外部とのインターフェースを提供するOpenAPIプロジェクトに最適です。もちろん、マイクロサービスの構築にも使えますし、豊富な あります。

RxCache :サンプルコード :

Read next

jQueryの基本機能

jQuery get a web element: は選択式で、コンストラクタ jQuery () に入れ、選択された要素を取得します。選択式はCSSセレクタでよく使われます。 jQueryのもう1つの設計思想は、最終的に選択された後にWeb要素に対して一連の処理を実行し、すべての処理を連鎖的にまとめて記述することです。 jQuer...

Jun 28, 2020 · 2 min read

デザインパターンの注意点

Jun 28, 2020 · 15 min read

Javaスクリプトを始める

Jun 28, 2020 · 3 min read

オープンソースの始まり

Jun 27, 2020 · 3 min read