ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
12.53k stars 1.02k forks source link

Bug in get method with jackson serialization #1445

Closed LifeIsStrange closed 3 years ago

LifeIsStrange commented 4 years ago

Ktor Version and Engine Used (client or server and name) version, client/server, additional ktor modules included

Describe the bug Jackson cant serialize a json into a model on Get but it works on Post with the exact same code.

route("/auth") {
                get() { //throw error at runtime
                    val post = call.receive<PostUser>()
                    call.respond(HttpStatusCode.Created)
                }
                post() { //works fine
                    val post = call.receive<PostUser>()
                    call.respond(HttpStatusCode.Created)
                }
            }

Expected behavior Ability to serialize body json in get method.

By sending in postman: GET http://0.0.0.0:8080/users/auth in raw json

{
    "user": {
        "id": "767e5d91-1f84-4761-9fe6-1efe176d0d7a",
        "username": "typescripst",
        "password": "strddingentz"
    }
}

I get the following error (unlike with post for the same request)

2019-11-17 00:15:30.104 [nioEventLoopGroup-4-1] ERROR Application - Unhandled: GET - /users/auth
com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
 at [Source: (InputStreamReader); line: 1, column: 0]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4145)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4000)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3049)
    at io.ktor.jackson.JacksonConverter.convertForReceive(JacksonConverter.kt:44)
    at io.ktor.features.ContentNegotiation$Feature$install$3.invokeSuspend(ContentNegotiation.kt:153)
    at io.ktor.features.ContentNegotiation$Feature$install$3.invoke(ContentNegotiation.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
    at io.ktor.request.ApplicationReceiveFunctionsKt.receive(ApplicationReceiveFunctions.kt:118)
    at com.back.ApplicationKt$module$5$2$3$1.invokeSuspend(Application.kt:109)
    at com.back.ApplicationKt$module$5$2$3$1.invoke(Application.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.features.StatusPages$interceptCall$2.invokeSuspend(StatusPages.kt:98)
    at io.ktor.features.StatusPages$interceptCall$2.invoke(StatusPages.kt)
    at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
    at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:180)
    at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:97)
    at io.ktor.features.StatusPages$Feature$install$2.invokeSuspend(StatusPages.kt:137)
    at io.ktor.features.StatusPages$Feature$install$2.invoke(StatusPages.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
    at io.ktor.routing.Routing.executeResult(Routing.kt:147)
    at io.ktor.routing.Routing.interceptor(Routing.kt:34)
    at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:99)
    at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:106)
    at io.ktor.features.ContentNegotiation$Feature$install$1.invoke(ContentNegotiation.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.features.CallLogging$Feature$install$2.invokeSuspend(CallLogging.kt:139)
    at io.ktor.features.CallLogging$Feature$install$2.invoke(CallLogging.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
    at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invokeSuspend(DefaultEnginePipeline.kt:118)
    at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
    at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invokeSuspend(NettyApplicationCallHandler.kt:36)
    at io.ktor.server.netty.NettyApplicationCallHandler$handleRequest$1.invoke(NettyApplicationCallHandler.kt)
    at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:55)
    at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:111)
    at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:154)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:54)
    at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
    at io.ktor.server.netty.NettyApplicationCallHandler.handleRequest(NettyApplicationCallHandler.kt:26)
    at io.ktor.server.netty.NettyApplicationCallHandler.channelRead(NettyApplicationCallHandler.kt:20)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374)
    at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:56)
    at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:365)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:163)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:416)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:515)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)

ps:

I've installed jackson with

install(DataConversion)
install(ContentNegotiation) {
        jackson {
            enable(SerializationFeature.INDENT_OUTPUT)
        }
    }
LifeIsStrange commented 4 years ago

I have the same problem with GSON

LifeIsStrange commented 4 years ago

So basically it is imossible to receive the body in a get request in Ktor? Isn't this a fundamental basic feature ?

imanushin commented 4 years ago

Could you please remove json feature and just receive text via method HttpResponse.readText? Probably, server returns invalid result. As I assume, server returns html page (with access denied content), which is not valid json response.

LifeIsStrange commented 4 years ago

I cant find HttpResponse.readText, for HttpResponse intellij auto import import io.netty.handler.codec.http.HttpResponse but cant find readText method. Isnt that just for the client ? I dont have a Ktor client, I use postman. How can I receive a get request with content type as application/json (I do not precise UTF8 is that wrong ?) as text inside my route ?

imanushin commented 4 years ago

@LifeIsStrange , oh, sorry, my fault. I thank you asked about Http Client. I felt json errors with it, this is reason of me here. And I caught real problem with uncompleted data, which is returned by Apache server.

In regards to your question, code is like this (please note - url is not valid):

        val responseText = HttpClient().use { client ->
            client.get<String>("https://github.com/ktorio/ktor/issues/1445") {
                accept(ContentType.Application.Json)
            }
        }

Of course client should be reused if possible. And please check here about Accept header.

oleg-larshin commented 3 years ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

Stexxe commented 3 years ago

Cannot reproduce with Ktor 1.6.1.