1

It is pretty typical in a RESTful API to see endpoints that can support multiple serialization formats:

// Sends back "fizz" resource that has an id=34 as a JSON object
http://api.example.com/v2/fizz/34.json

// Sends back "fizz" resource that has an id=34 as an XML object
http://api.example.com/v2/fizz/34.xml

// Sends back "fizz" resource that has an id=34 as a binary object,
// say, using Google Protocol Buffers
http://api.example.com/v2/fizz/34.bin

I'm designing a Dropwizard service, and am trying to figure out how to implement multiple format support, but the docs are barren in this regard. Any ideas?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
smeeb
  • 27,777
  • 57
  • 250
  • 447

1 Answers1

6

Generally support for different format configuration is simply done declaratively in the @Produces and @Consumes annotations. When the client sends data, then the Content-Type request header should be set to the actual type of the data being sent.

For example if the client is sending JSON data, then the client should set the request header Content-Type: application/json. Jersey will look for a method or class with @Consume("application/json"). If it finds it, then that means the app is configured to support the media type application/json. If not, then the client will get a response back saying the media type is not supported.

Likewise when the client is requesting data, it should set the Accept: application/json request header. Jersey will look for @Produces("application/json"). If it can't find it for the endpoint, then the client will get a message saying it is not an acceptable type.

So we can support different media types for the same endpoint. You can declare the method like

@Produces({"application/json", "application/xml", "application/x-protobuf"})
public Response getFoo() {
    return Response.ok(new Foo());
} 

The thing that you need to be concerned about is if there is a MessageBodyWriter for each of those media types that can handle the serialization of the Foo type. See more at JAX-RS Entity Providers.

Alternatively, if the application/json, application/xml, application/x-protobuf media types require different domain types to be serialized, you can have different methods to handle the different types. For example, application/json and application/xml, you can usually get away with using the same Foo domain object, so you can just do

@Produces({"application/xml", "application/json"})
public Response getFooJsonOrXml() {
    return Response.ok(new Foo());
}

But for Protobuf, it requires Protobuf compiled classes. So instead of the Foo domain object, you would return the generated type.

@Produces("application/x-protobuf")
public Response getFooProtobuf() {
    return Response.ok(new ProtobufFoo());
}

As for your use of the .xml, .json extension in your URLs, that is generally not the way for the client to say what type it wants. Usually the set the Accept request header to one of the types that your server supports, as mentioned above.

But there is support for the URL type extension, but it is usually meant for clients that don't have access to setting the headers, for instance a browser. But you need to configure those media type mappings with the UriConnegFilter. For example

Map<String, MediaType> map = new HashMap<>();
map.put("xml", MediaType.APPLICATION_XML_TYPE);
map.put("json", MediaType.APPLICATION_JSON_TYPE);
map.put("bin", ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE);
env.jersey().property(ServerProperties.MEDIA_TYPE_MAPPINGS, map);

See Also:

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720