korlibs / korge

KorGE Game Engine. Multiplatform Kotlin Game Engine
https://korge.org/
Other
2.55k stars 125 forks source link

Quotes should not be implicitly escaped when request body is being stringified #539

Open Kietyo opened 2 years ago

Kietyo commented 2 years ago

Example:

    suspend fun putData(): Any {
        val response = restClient.put(
            "users/bob/name.json",
            """
                { "first": "Jack", "last": "Sparrow" }
            """.trimIndent()
        )
        return response
    }

Example test:

    @Test
    fun putData() = runBlockingNoJs {
        val datastore = DatastoreKorge()

        val data = datastore.putData()

        println(data)
    }

This makes it unusable for firebase database:

image
Kietyo commented 2 years ago

The code that does this: commonMain/com/soywiz/korio/serialization/json/Json.kt:172

soywiz commented 2 years ago

RestClient receives an Any as the payload parameter, not a String. It is converting the object into a string. So if you passes a string as parameter, it will be converted to a JSON string, and that will quote.

Instead, you should pase an object, maps and/or lists. For example:

        val response = restClient.put(
            "users/bob/name.json",
            mapOf("first" to "Jack", "last" to "Sparrow")
        )
        return response

or:

data class User(val first: String, val last: String)
        val response = restClient.put(
            "users/bob/name.json",
            User(first = "Jack", last = "Sparrow")
        )
        return response
Kietyo commented 2 years ago

I don't think it should be implicitly converted to a JSON string though.

I believe this behavior should be made more explicit. A suggestion I have is to use value classes and then use a when statement to treat the content differently.:

@JvmInline
value class RawString(val str: String)

suspend fun request(method: Http.Method, path: String, request: Any?, mapper: ObjectMapper = Mapper): Any {
    val requestContent = request?.let { 
        when (it) {
            is RawString -> ... 
            else -> Json.stringifyTyped(it, mapper) 
        }
    }
  ...
}
Kietyo commented 2 years ago

A workaround for anyone that runs into this issue. Use HttpClient instead.

suspend fun putData2(): Any {
    return client.request(
        Http.Method.PUT,
        "https://<redacted>.com/users/mike/name.json",
        content =             """
            { "first": "Jack", "last": "Sparrow" }
        """.trimIndent().openAsync()
    )
}