Anyways I'm the author of the issue here: <https:/...
# ktor
m
Anyways I'm the author of the issue here: https://github.com/ktorio/ktor/issues/475
d
Hey
sure, let’s check this out
do you have the full sample?
@martmists
I have managed to reproduce it with:
Copy code
package com.example

import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.jackson.*
import io.ktor.pipeline.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.coroutines.experimental.io.*
import kotlinx.coroutines.experimental.io.jvm.javaio.*
import java.net.*


fun main(args: Array<String>) {
    //val handler = DatabaseHandler()
    embeddedServer(Netty, port = 8080) {
        install(ContentNegotiation) {
            jackson {
            }
            register(ContentType.Application.FormUrlEncoded, BodyConverter())
        }
        routing {
            // ...
            post("/account") {
                val post = call.receive<CreateAccountContent>()
                call.respondText("id=${post.id}", ContentType.Application.FormUrlEncoded, HttpStatusCode.OK)
            }
        }
    }.start(wait = true)
}

data class CreateAccountContent(
    val id: Int,
    val password: String
)

class BodyConverter : ContentConverter {
    override suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any? {
        val request = context.subject
        val channel = request.value as? ByteReadChannel ?: return null
        val reader = channel.toInputStream().reader(context.call.request.contentCharset() ?: Charsets.UTF_8)
        val result = mutableMapOf<String, Any>()
        for (pair in reader.readText().split('&')) {
            val strings = pair.split("=")
            val decoded = URLDecoder.decode(strings[1], "UTF-8")
            val arg = try {
                decoded.toInt()
            } catch (e: Exception) {
                decoded
            }
            result[strings[0]] = arg
        }
        println(result.map { e -> "${e.key} -> ${e.value}" })
        
        return result.toMap()
    }

    override suspend fun convertForSend(
        context: PipelineContext<Any, ApplicationCall>,
        contentType: ContentType,
        value: Any
    ): Any? {
        TODO()
    }
}
And I have found the issues: 1. You are not setting the content-type of the response 2. If you check a sample of
ContentConverter
, for example the Jackson one, or the gson one ( https://ktor.io/features/content-negotiation.html ), you don’t have to return a map, but the object actually requested.
Copy code
package com.example

import com.fasterxml.jackson.module.kotlin.*
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.jackson.*
import io.ktor.pipeline.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.coroutines.experimental.io.*
import kotlinx.coroutines.experimental.io.jvm.javaio.*
import java.net.*


fun main(args: Array<String>) {
    //val handler = DatabaseHandler()
    embeddedServer(Netty, port = 8080) {
        install(ContentNegotiation) {
            jackson {
            }
            register(ContentType.Application.FormUrlEncoded, BodyConverter())
        }
        routing {
            // ...
            post("/account") {
                val post = call.receive<CreateAccountContent>()
                call.respondText("id=${post.id}", ContentType.Application.FormUrlEncoded, HttpStatusCode.OK)
            }
        }
    }.start(wait = true)
}

data class CreateAccountContent(
    val id: Int,
    val password: String
)

class BodyConverter : ContentConverter {
    val objectMapper = jacksonObjectMapper()
    
    override suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any? {
        val request = context.subject
        val channel = request.value as? ByteReadChannel ?: return null
        val reader = channel.toInputStream().reader(context.call.request.contentCharset() ?: Charsets.UTF_8)
        val result = mutableMapOf<String, Any>()
        for (pair in reader.readText().split('&')) {
            val strings = pair.split("=")
            val decoded = URLDecoder.decode(strings[1], "UTF-8")
            val arg = try {
                decoded.toInt()
            } catch (e: Exception) {
                decoded
            }
            result[strings[0]] = arg
        }
        println(result.map { e -> "${e.key} -> ${e.value}" })

        return objectMapper.convertValue(result.toMap(), request.type.javaObjectType)
    }

    override suspend fun convertForSend(
        context: PipelineContext<Any, ApplicationCall>,
        contentType: ContentType,
        value: Any
    ): Any? {
        TODO()
    }
}
-
call.respondText("id=${post.id}", ContentType.Application.FormUrlEncoded, HttpStatusCode.OK)
-
val objectMapper = jacksonObjectMapper()
-
return objectMapper.convertValue(result.toMap(), request.type.javaObjectType)
that should do
m
what
d
let me know tomorrow if it worked for you, and I guess you can close the issue. I’m going to check the documentation to see if we can make this more obvious
m
I'm not trying to respond as FormUrlEncoded
d
oh, true
m
I'm trying to parse one
d
then only the part of the objectMapper + conversion
m
yeah that worked, thanks
👍 1