Kong / unirest-java

Unirest in Java: Simplified, lightweight HTTP client library.
http://kong.github.io/unirest-java/
MIT License
2.58k stars 591 forks source link

Jackson MismatchedInputException for successful response with no body, after migrating to Unirest 4 #502

Closed j-mew-s closed 7 months ago

j-mew-s commented 8 months ago

Describe the bug Receiving a successful response (I tried 200 and 204) with no body fails deserialization. I guess this might just be a Jackson or Java Http Client issue, but it feels like a pretty basic thing. There's a fair chance I've configured something wrong! It seems that with the Apache client, the response entity was set to null, whereas now it's set to empty string (as highlighted in the migration notes).

The impact is that previously in the ObjectResponse constructor, the getBody call would be skipped due to the Optional.empty, and ultimately Jackson wouldn't try to map anything. When it does, it falls over.

To Reproduce build.gradle.kts

plugins {
    kotlin("jvm") version "1.9.20"
    id("java-library")
    application
}

group = "co.glean"
project.setProperty("mainClassName", "co.glean.common.main.MainKt")
defaultTasks = mutableListOf("build")

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(kotlin("test"))
    testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0")

    // failing with unirest 4
    implementation(platform("com.konghq:unirest-java-bom:4.1.1"))
    implementation("com.konghq:unirest-java-core")
    implementation("com.konghq:unirest-objectmapper-jackson")

    // working with unirest 3
    //implementation("com.konghq:unirest-java:3.14.2")
    //implementation("com.konghq:unirest-objectmapper-jackson:3.14.4")
    //implementation("com.fasterxml.jackson.core:jackson-databind:2.14.1")
    //implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.1")

}

tasks.test {
    useJUnitPlatform()
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

TestMain.kt

import kong.unirest.core.*
//import kong.unirest.*

import com.fasterxml.jackson.databind.json.JsonMapper
import kong.unirest.jackson.JacksonObjectMapper
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.jupiter.api.Test

class TestUnirest411 {
    @Test
    fun `test unirest`() {
        Unirest.config().objectMapper = JacksonObjectMapper(JsonMapper.builder().build())

        val mockWebServer = MockWebServer()
        try {
            mockWebServer.start()
            mockWebServer.enqueue(MockResponse().setResponseCode(204))

            val response = Unirest.post(mockWebServer.url("/test").toString())
                .asObject(Empty::class.java)

            if (!response.isSuccess) {
                val parsingException: UnirestParsingException? = response.parsingError.orElse(null)
                throw Error("${response.status}", parsingException)
            }
        } finally {
            mockWebServer.shutdown()
        }
    }
}

stack trace

kong.unirest.core.UnirestParsingException: kong.unirest.core.UnirestException: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
 at [Source: (String)""; line: 1, column: 0]
    at app//kong.unirest.core.BaseResponse.setParsingException(BaseResponse.java:97)
    at app//kong.unirest.core.ObjectResponse.getBody(ObjectResponse.java:68)
    at app//kong.unirest.core.ObjectResponse.lambda$new$1(ObjectResponse.java:41)
    at java.base@17.0.9/java.util.Optional.map(Optional.java:260)
    at app//kong.unirest.core.ObjectResponse.<init>(ObjectResponse.java:41)
    at app//kong.unirest.core.BaseRequest.lambda$asObject$6(BaseRequest.java:242)
    at app//kong.unirest.core.java.JavaClient.transformBody(JavaClient.java:160)
    at app//kong.unirest.core.java.JavaClient.request(JavaClient.java:72)
    at app//kong.unirest.core.BaseRequest.request(BaseRequest.java:341)
    at app//kong.unirest.core.BaseRequest.asObject(BaseRequest.java:242)
    at app//TestUnirest411.test unirest(TestMain.kt:21)

Expected behavior No parsing exception when a 204 response has no body.

Environmental Data: see gradle file

ryber commented 8 months ago

You are essentially asking Jackson to map "" to a "Empty" object, which it cannot do, because "" is not valid JSON. Unirest has a method for this case called .asEmpty(), which has no body at all. I would recommend that

j-mew-s commented 7 months ago

Thank you for your quick response, I'll give that a try.