问题
Please note: Although this question specifically mentions Dropwizard, I believe anyone with Jersey/JAX-RS experience should be able to answer this question, as I would imagine Dropwizard is just following Jersey/JAX-RS conventions under the hood.
I have a Dropwizard service that reds/writes in JSON and works beautifully.
I would like to now switch it to read/write binary data (to minimize network bandidth). I see there is the Dropwizard-Protobuf lib but I have a few concerns about implementing binary serialization in Dropwizard.
First off, here's the important stuff from my current (JSON-centric) code:
// Groovy pseudo-code
// Domain entity/POJO
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
class Fizz {
@JsonProperty
String name
@JsonProperty
boolean isBuzz
}
// The Dropwizard app entry point
class FizzService extends Application<FizzConfiguration> {
@Override
void run(FizzConfiguration fizzCfg, Environment env) throws Exception {
// ... lots of stuff
env.jersey().register(new FizzService())
}
}
// JAX-RS resource with a sample GET endpoint
@Path(value = "/fizz")
@Produces(MediaType.APPLICATION_JSON)
class FizzResource {
@GET
@Path("/{id}")
Fizz getFizzById(@PathParam("id") int id) {
// Look up a 'Fizz' in a DB and return it.
lookupFizzinDB(id)
}
}
So as you can see, the GET /fizz
endpoint expect a JSON request entity that contains an element called id
of type int
. It returns a Fizz
response entity that matches the provided id
.
I want to switch this from JSON to binary via Google Protocol Buffers.
According to the Dropwizard-Protobuf docs, this is as simple as just adding this to my FizzService#run(...)
method:
environment.jersey().register(new ProtocolBufferMessageBodyProvider())
The problem is that currently my whole app is wired to serialize/deserialize to/from JSON. The @JsonProperty
annotations on my Fizz
class have meaning to Dropwizard. The @Produces(MediaType.APPLICATION_JSON)
annotation on the FizzResource
also plays a critical role. I'm worried that making my Dropwizard app read/write protobuf-generated binary is not as easy as the 1-liner posted in the docs.
I'm not married to this library. If anyone has any experience setting up REST endpoints in a Dropwizard app to accept/receive protobuf-generated binary, all I care about is a working, efficient solution. Ideas?
回答1:
You're right, it's not as easy as the one liner. You need to have protobuf generate code for it to work. Check out the Protocol Buffers Documentation. You first need to have a proto file that you compile with the protobuf compiler, which generates the code for you. This generated code is what you use to build your domain/model objects. The protobuf provider from Dropwizard works off this compiled code. Whether or not you use the Dropwizard provider, you well still need to use the generated code. See the section "How do I start" in the above link.
After you have the generated code, then in your resource method, the generated class/type is what you will need to return for the provider to be able to serialize it. You will also need to have @Produces("application/x-protobuf")
or @Produces(ProtocolBufferMediaType.APPLICATION_PROTOBUF) on your resource method or resource class, so Jersey knows how to find the provider for the media type.
You can support both application/json
and application/x-protobuf
, as you can have more that one media type in the @Produces
. Just use the syntax @Produces({ .. , .. })
.
That's not all though. Since you will need to return two different types, i.e your simple POJO for JSON, or the generated type for Protobuf, you will either need to check for the header in the resource method
@Produces({"application/json", "application/x-protobuf"})
public Response getFoo(@Context HttpHeaders headers) {
List<MediaType> accepts = headers.getAcceptableMediaTypes();
if (accepts.contains(MediaType.APPLICATION_JSON_TYPE) {
return Response.ok(new Foo());
} else if (accepts.contains(ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE) {
return Reponse.ok(new ProtoBufFoo());
} else {
// default
return Response.ok(new Foo());
}
}
Or you can have two different method, one for each type
@Produces("application/json")
public Response getFooJson() {
return Response.ok(new Foo());
}
@Produces("application/x-protobuf")
public Response getFooProto() {
return Response.ok(new ProtoBufFoo());
}
Whatever the client sends as its Accept
header, that is the type that will be sent out. For example Accept: application/json
or Accept: application/x-protobuf
See Also:
- Combining Google proto buffers with Jersey/JAX-RS
来源:https://stackoverflow.com/questions/32510936/dropwizard-and-protocol-buffers-by-example