Hi, is there a way to replace headers in ktor? I ...
# ktor
s
Hi, is there a way to replace headers in ktor? I initialize client as below
Copy code
HttpClient(OkHttp) {
            engine {
                preconfigured = okHttpClient
            }
            defaultRequest {
                url("<http://host.com|host.com>")

                // this adds content-type = application/json to all requests
                contentType(ContentType.Application.Json)
            }
now in one of the request i want to change the
content-type
as
application/xml
Copy code
client.get("endpoint"){

      // doing this adds two content-type json and xml
      headers.append("content-type", "application/xml") 

      // this also adds two typos
      headers["content-type"] = acceptHeader
}
s
headers.set()
ought to do it.
s
doesn't work unfortunately. both the headers are appended in this case "application/json,application/xml"
s
That's odd. I just wrote a test-case for it, and it works as I expect it.
Copy code
@Test
    fun `test requests`() = runTest {
        val engine = MockEngine { respondOk() }

        val client = HttpClient(engine) {
            defaultRequest {
                contentType(ContentType.Application.Json)
            }
        }

        // using default param
        client.get<String>("url")
        engine.requestHistory.last().headers.apply {
            get(HttpHeaders.ContentType) shouldBeEqualTo "application/json"
        }

        // overwriting param
        client.get<String>("url") {
            headers.set(HttpHeaders.ContentType, "application/xml")
        }
        engine.requestHistory.last().headers.apply {
            get(HttpHeaders.ContentType) shouldBeEqualTo "application/xml"
        }
    }
Did you maybe accidentally leave the
headers.append()
in the code? Or have another piece of code that might add such a header?
s
I tried writing below test. it's failing
Copy code
@Test
    fun `test requests`() = runTest {
        val engine = MockEngine { respondOk() }

        val client = HttpClient(engine) {
            defaultRequest {
                // send request body as json
                contentType(ContentType.Application.Json)
                headers.appendIfNameAbsent(HttpHeaders.AcceptLanguage, Locale.getDefault().language)
            }
        }

        // using default param
        client.get("url")
        engine.requestHistory.last().headers.apply {
            assertEquals(get(HttpHeaders.ContentType), "application/json")
        }

        // overwriting param
        client.get("url") {
            headers.set(HttpHeaders.ContentType, "application/xml")
        }
        engine.requestHistory.last().headers.apply {
            assertEquals(get(HttpHeaders.ContentType), "application/xml")
        }
    }
Copy code
expected: <application/json> but was: <application/xml>
Expected :application/json
Actual   :application/xml
with ktor version 2.3.3
s
Welp, it's a regression. I'm on 2.3.2 and it's fine. I switched to 2.3.3 and I get the same wrong behavior.
r
as a workaround, you can replace
contentType(ContentType.Application.Json)
inside
defaultRequest {}
block with
headers.appendIfNameMissing(HttpHeaders.ContentType, ContentType.Application.Json.toString())
s
I tried it. I replaces the
content-type
but doesn't change
Accept
Copy code
defaultRequest {
                url("baseurl")
                // send request body as json
                headers.appendIfNameAbsent(HttpHeaders.ContentType, ContentType.Application.Json.toString())
                headers.appendIfNameAbsent(HttpHeaders.Accept, ContentType.Application.Json.toString())
            }
Copy code
client.get("endpoint") {
            headers.appendIfNameAbsent(Accept, "test/csv")
            headers.appendIfNameAbsent(HttpHeaders.ContentType, "text/csv")
        }.body()
in the request headers i get both json and xml appended
Copy code
accept: text/csv,application/json
content-type: test/csv
r
Do you use
ContentNegitiation
plugin? It will add
Accept
header for all the serializers that you register with it.
s
yes. what should i do in this case?
Copy code
install(ContentNegotiation) {
    json(Json))
}
r
Is it a problem that you send two
Accept
headers? It is not against the spec to do it
s
yes the desired behaviour is to send either of "text/csv" or "application/json" for a particular endpoint we have.
r
Currently, there is no way to disable
ContentNegotiation
for some plugins. Should be an easy fix. Please create a feature request in YT. For now, you may need to create a separate client for this endpoint.
👍 1
187 Views