この機能は、Content-Type
とAccept
ヘッダーに応じて自動でのコンテンツの変換を行う機能です。
io.ktor.features.ContentNegotiation
and no additional artifacts are required.
ContentNegotiation Featureを使うとカスタムのコンバーターを登録・設定することができます。
install(ContentNegotiation) {
register(MyContentType, MyContentTypeConverter()) {
// Optionally configure the converter...
}
}
例えば:
install(ContentNegotiation) {
register(ContentType.Application.Json, JacksonConverter())
}
直接扱うことができないオブジェクトでレスポンスを返したいとき、例えばカスタムのデータクラスを使うとき、
この機能はクライアントのAccept
ヘッダーをチェックし、
どのContent-Type
が利用されるかと、そしてどのContentConverter
が呼ばれるかを決定します。
call.respond(MyDataClass("hello", "world"))
たった今、送信時にサポートされている唯一のコンテントネゴシエーションの戦略は、クライアントのAccept
ヘッダーを使うものです。
その他の戦略を実装する上では問題があります。
受信時にはリクエストのContent-Type
が使われ、どのContentConverter
がリクエストを処理するのに使われるのかが決定されます。
val myDataClass = call.receive<MyDataClass>()
自身でコンバーターを書きたいのなら、ContentConverter
インターフェースを実装する必要があります。
interface ContentConverter {
suspend fun convertForSend(context: PipelineContext<Any, ApplicationCall>, contentType: ContentType, value: Any): Any?
suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any?
}
例えば、GsonConverter
の実装は以下のようになっています:
class GsonConverter(private val gson: Gson = Gson()) : ContentConverter {
override suspend fun convertForSend(context: PipelineContext<Any, ApplicationCall>, contentType: ContentType, value: Any): Any? {
return TextContent(gson.toJson(value), contentType.withCharset(context.call.suitableCharset()))
}
override suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any? {
val request = context.subject
val channel = request.value as? ByteReadChannel ?: return null
val reader = channel.readRemaining().readText((context.call.request.contentCharset() ?: Charsets.UTF_8).newDecoder()).reader()
return gson.fromJson(reader, request.type.javaObjectType)
}
}
Ktorはいくつかの初めから利用可能なコンテントコンバーターを提供しています。
application/json