Prateek Kumar
04/09/2024, 2:03 PMtransformResponseBody{ }
, work using createClientPlugin
It works if put before ContentNegotiation
plugin, but any plugin with transformResponseBody
put after ContentNegotiation
dosen’t get called at all.
And in my other projects , so far i have been unsuccessful in making transformResponseBody
work.
Every other method provided by createClientPlugin
works without problem.
Is there something i am missing or there is some extra step needed to make it work?Aleksei Tirman [JB]
04/09/2024, 2:21 PMPrateek Kumar
04/09/2024, 2:23 PMContentNegotiation
So was trying to achieve it with transformResponseBody
, but i don’t get any callback in this methodPrateek Kumar
04/09/2024, 2:29 PMonResponse
get’s called for every single installed Custom plugin , but not the case with transformResponseBody
,This should not be the case , Right? Or am i missing somethingAleksei Tirman [JB]
04/09/2024, 2:38 PMtransformResponseBody
's block is called only when the response body is still the ByteReadChannel
(it isn't transformed to the specified type). The order of the plugins determines which transformation come first since both use the same pipeline phase. You can insert the phase before the HttpResponsePipeline.Transform
phase to make your transformation executed first. Here is an example:
fun main(): Unit = runBlocking {
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
}
val beforeTransform = PipelinePhase("BeforeTransform")
client.responsePipeline.insertPhaseBefore(HttpResponsePipeline.Transform, beforeTransform)
client.responsePipeline.intercept(beforeTransform) { (typeInfo, body)->
// Decrypt the body here and then pass it to proceedWith(...)
println(body)
proceedWith(HttpResponseContainer(typeInfo, body))
}
val response = client.get("<https://httpbin.org/get>")
println(response.body<HttpBin>())
}
@Serializable
data class HttpBin(val origin: String)
Prateek Kumar
04/09/2024, 3:06 PMtransformResponseBody
, But at the same time i am able to read content using onResponse
very easily.
What can be the reason for that,i tried to print
it.content.toString
inside onResponse, Output is ByteBufferChannel(92329252, IDLE(with buffer))
I will use the implementation provided by you to make it work , Still wondering why transformResponseBody
didn’t workAleksei Tirman [JB]
04/09/2024, 3:59 PMonResponse
is executed before the HttpResponse.body
method is called.adam-mcneilly
12/04/2024, 12:43 AM({ myJson })
I was hoping the easiest way to serialize this properly was to intercept it, look if it started and ended with parens, and remove them from the string. This solution seems pretty close, but I'm unclear how to manipulate the byte array here:
// Decrypt the body here and then pass it to proceedWith(...)
println(body)
Do you have any examples of this?Aleksei Tirman [JB]
12/04/2024, 9:58 AM@OptIn(DelicateCoroutinesApi::class)
suspend fun ByteReadChannel.transform(): ByteReadChannel {
val body = this
val first = body.readByte()
val second = body.readByte()
if (first != '('.code.toByte() || second != '{'.code.toByte()) {
return GlobalScope.writer {
channel.writeByte(first)
channel.writeByte(second)
body.copyTo(channel)
}.channel
}
return GlobalScope.writer {
while (!body.isClosedForRead) {
val arr = body.readRemaining(8 * 1024).readByteArray()
if (body.isClosedForRead && arr.size >= 2) {
val first = arr[arr.size - 2]
val second = arr[arr.size - 1]
if (first == '}'.code.toByte() && second == ')'.code.toByte()) {
channel.writeFully(arr, 0, arr.size - 2)
break
} else {
channel.writeFully(arr)
continue
}
}
channel.writeFully(arr)
}
}.channel
}
Here is a usage example:
client.responsePipeline.intercept(beforeTransform) { (typeInfo, body)->
when (body) {
is ByteReadChannel -> {
proceedWith(HttpResponseContainer(typeInfo, body.transform()))
}
else -> TODO()
}
}
adam-mcneilly
12/05/2024, 4:44 AM