I had a requirement to produce and consume protobuf messages through a Jersey web service, either as their binary representation, or as JSON. Helpfully, the protobuf-java-util
artefact contains the JsonFormat
class that handles the proto-to-JSON and JSON-to-proto side of things.
An elegant approach when using Jersey is to create (and register) MessageBodyWriter
and MessageBodyReader
providers. The appropriate implementation will be used based on the Accept
and Content-type
header the HTTP client sends.
Your service code is as simple as:
@GET
@Path("/hello")
public Common.Event test() {
return Common.Event.newBuilder().setIdentifier("Hello World").build();
}
...and, to consume
@POST
@Path("/hello")
public Common.Event testPost(Common.Event input) {
return Common.Event.newBuilder().setIdentifier("You said: " + input.getIdentifier()).build();
}
Registering the providers with Jersey looks like the below.
ResourceConfig rc = new ResourceConfig();
// Resources can produce either application/x-protobuf or application/json (Accept header)
rc.register(ProtobufBinaryMessageWriter.class);
rc.register(ProtobufJsonMessageWriter.class);
// Resource that take a body can consume application/json
rc.register(ProtobufJsonMessageReader.class);
An HTTP header, x-proto-type
is added to the response. This is the fully qualified name of the message, which would be used by the client to look up a descriptor in order to parse the response. As JSON responses are self-describing this isn't really required.
proto3 has an Any
type that allows messages of, well, any type, to be embedded. In order for the JSON serializer to work, you need to register the descriptors of possibly enclosed message types. See line 30 of ProtobufJsonMessageWriter.java
The classes within this gist are adapted from various older (possibly proto2) implementations that you'll probably have also found by Googling this.
Note that I have only implemented consumption of JSON as I had no requirement to accept a binary protobuf through a POST request at this point.
I have messages on Kafka topics as protobufs. I am using the interactive queries feature in Kafka Streams to expose a Kafka Streams state store as a REST service. Browsers may access this REST service so need JSON. Due to the architecture of Kafka Streams IQ, I also need to support service-to-service communication so that a request for a key held on another instances' state store can be proxied back to the caller. protobuf seemed to be the natural serialization to use here instead of JSON. If you're interested in Kafka Streams interactive queries, see here: https://kafka.apache.org/20/documentation/streams/developer-guide/interactive-queries.html