https://kotlinlang.org logo
#ktor
Title
# ktor
d

df

11/17/2020, 5:08 PM
is there any way to just write plain text into the body of a post request with the ktor client? I have the following code
Copy code
val content = """
            {"identifier":"cdp_9055274"}
            {"identifier":"cdp_9111757"}
            {"identifier":"cdp_9135021"}
        """.trimIndent()

        val response = httpClient.patch<String> {
            url("/api/rest/v1/products")
            header("Authorization", "Bearer ${token.accessToken}")
            contentType(ContentType("application", "vnd.akeneo.collection+json"))
            body = content
        }
and it seems like regardless of what I'm writing into the body (the string, TextContent / ByteArrayContent), the body is always serialized into json again.
m

marstran

11/17/2020, 5:22 PM
How is
httpClient
defined? Maybe you can try with a client where the JSON-feature isn't installed.
d

df

11/17/2020, 5:23 PM
of course that would work.. it just feels pretty bad to have two clients injected because one of the requests requires a non-json body
Copy code
fun httpClient(objectMapper: ObjectMapper, settings: AkeneoClientSettings): HttpClient {
        return HttpClient(CIO) {
            defaultRequest {
                url {
                    host = settings.host
                    protocol = URLProtocol.HTTPS
                }
            }
            install(JsonFeature) {
                serializer = JacksonSerializer(objectMapper) {
                    propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE
                }
            }
            install(Logging) {
                logger = Logger.SIMPLE
                level = LogLevel.ALL
            }
            expectSuccess = false
        }
    }
that's the client definition
and tbh i don't really understand why it's serializing the body again. The json feature should only kick-in for application/json content types
okay the last issue is explained by
value.startsWith("application/") && value.endsWith("+json")
m

marstran

11/17/2020, 5:29 PM
Right, maybe you could set the Content-Type header to
text/plain
?
d

df

11/17/2020, 5:30 PM
unfortunately the server rejects requests if the wrong content-type header is sent
m

marstran

11/17/2020, 5:31 PM
Hmm, how is that wrong if what you want is to send plain text content?
d

df

11/17/2020, 5:31 PM
Content-type • Equal to 'application/vnd.akeneo.collection+json', no other value allowed
because that's how it's documented.
m

marstran

11/17/2020, 5:31 PM
Ok
d

df

11/17/2020, 5:31 PM
In their definition it seems to be "some kind of json" if you have one json object serialized per line
instead of having a proper json array
m

marstran

11/17/2020, 5:32 PM
What exactly happens to the body when you send the string with that content-type?
d

df

11/17/2020, 5:33 PM
Copy code
HttpClient: CONTENT HEADERS
HttpClient: BODY Content-Type: application/vnd.akeneo.collection+json
HttpClient: BODY START
HttpClient: "{\"identifier\":\"cdp_9055274\"}\n{\"identifier\":\"cdp_9111757\"}\n{\"identifier\":\"cdp_9135021\"}"
okay what seems to work (for now) is the following
Copy code
install(JsonFeature) {
                serializer = JacksonSerializer(objectMapper) {
                    propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE
                    receiveContentTypeMatchers = listOf(
                        object : ContentTypeMatcher {
                            override fun contains(contentType: ContentType): Boolean {
                                return ContentType.Application.Json.match(contentType)
                            }
                        }
                    )
                }
            }
i still think the design could be improved. I don't see a reason why we can't have a OutgoingContent implementation that is not touched by any serializer.. sth. like RawContent(body: ByteArray)
r

Rustam Siniukov

11/17/2020, 6:32 PM
did you try something like this?
Copy code
val response = httpClient.patch<String> {
            url("/api/rest/v1/products")
            header("Authorization", "Bearer ${token.accessToken}")
            body = TextContent(content, ContentType("application", "vnd.akeneo.collection+json"))
        }
d

df

11/18/2020, 9:17 PM
I did but it still applies the json serialization to the body
r

Rustam Siniukov

11/23/2020, 1:28 PM
d

df

11/23/2020, 7:42 PM
Of course. Do you just want me to describe my use-case? I'm not sure if one of the things I tried should have worked and is a bug or if everything works as intended and my use-case is simply not covered.
r

Rustam Siniukov

11/23/2020, 7:50 PM
Example that I showed should not be intercepted by the serializer. But please make sure that you don’t set
ContentType
in header, only in
TextContent
. But I think that maybe it makes sense to skip serialization for all children of
OutgoingContent
. Ticket will be helpful ti think about usecases and agree on the solution
10 Views