RCHowell / rchowell.github.io

https://rchowell.github.io
0 stars 0 forks source link

Ktor Protobuf Serialization and Deserialization #16

Open RCHowell opened 3 years ago

RCHowell commented 3 years ago

I enjoy using Protobufs to generate data objects in different languages with minimal effort, and I want to use Ktor as a RESTful service framework. Consider Swagger as an alternative. This is what is required for protobuf serde with Ktor. I wasn't able to find this in the Ktor docs.

Converter

class ProtobufConverter : ContentConverter {
    override suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any? {
        val request = context.subject
        val channel = request.value as? ByteReadChannel ?: return null
        return channel.readRemaining().readBytes()
            .let {
                request.type.javaObjectType.getMethod("parseFrom", ByteArray::class.java).invoke(null, it)
            }
    }

    override suspend fun convertForSend(
        context: PipelineContext<Any, ApplicationCall>,
        contentType: ContentType,
        value: Any
    ): Any? {
        if (value !is MessageLite) return null
        return ByteArrayContent(value.toByteArray(), contentType)
    }
}

Content Negotiation and Routing

fun Application.configueRouting(service: ServiceImplementation) {
    install(ContentNegotiation) {
        register(ContentType.Application.ProtoBuf, ProtobufConverter()) // application/protobuf
    }
    routing {
        post("/my-api") {
            val req = call.receive<MyReq>()    // MyReq protobuf message
            val res = service.myApi(req)       // MyRes protobuf message
            call.respond(res)
        }
    }
}