ktor icon indicating copy to clipboard operation
ktor copied to clipboard

Content negotiation module is ignoring Accept directives

Open adam-arold opened this issue 5 years ago • 2 comments

I'm trying to limit the ContentTypes which can be returned form my Ktor endpoint (this is just an example):

accept(ContentType.Text.JavaScript) {
    get(operation.route) {
        call.respondText("foo" )
    }
}

but Ktor simply ignores ContentType.Text.Javascript even if I don't send that header form the browser.

adam-arold avatar Jul 20 '20 14:07 adam-arold

Could you please provide a simplified sample? the accept function definitely shouldn't just ignore the missing header and shouldn't pass to the get handler. Is there any chance that it is actually handled by another handler or something is cached somewhere in the browser?

cy6erGn0m avatar Jul 20 '20 14:07 cy6erGn0m

This was discussed on the Kotlin Slack here.

So what happened is that since there was no error handler, Ktor simply swallowed the exception which was thrown. The problem is that the API is not well documented and it is not obvious what receive, receiveParameters, parameters are for.

This is an example which you can use to reproduce this:

import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.application.install
import io.ktor.features.ContentNegotiation
import io.ktor.http.ContentType
import io.ktor.request.receive
import io.ktor.response.respondText
import io.ktor.routing.accept
import io.ktor.routing.get
import io.ktor.routing.routing
import io.ktor.serialization.json
import io.ktor.server.engine.applicationEngineEnvironment
import io.ktor.server.engine.connector
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import kotlinx.serialization.Serializable
fun main(args: Array<String>) {
    val env = applicationEngineEnvironment {
        module {
            example()
        }
        connector {
            port = 8080
            host = "0.0.0.0"
        }
    }
    embeddedServer(Netty, env).apply {
        start(wait = true)
    }
}
@Serializable
data class PostURL(
        val date: String,
        val titleSlug: String,
        val redirectTo: String? = null
)
fun Application.example() {
    install(ContentNegotiation) {
        json()
    }
    routing {
        accept(ContentType.Any) {
            get("/foo/{date}/{titleSlug}") {
                val input = call.receive(PostURL::class)
                println("$input was received.")
                call.respondText("foo")
            }
        }
    }
}

There should be a way to deserialize a path to a data structure without having to use the @Location annotation since ktor might not be present in the codebase where the data structure is.

It should also be clarified with easy to understand examples in the Ktor documentation how these methods work together and what are their limitations.

adam-arold avatar Jul 21 '20 13:07 adam-arold