6

I read a lot about versioning REST APIs, f.e. in this thread: Best practices for API versioning?

Because of that I would like to use the HTTP-Accept-Header to indicate which version the client is asking for. But how can I apply this in my application? Which changes had therefore be made? How does the marshaller know, which version should be used? Do I have to register my type?

What I know is that I have to change the content of the @Produces-Annotation

@GET
@Path("/locations")
@Produces("application/vnd.mycompany-v1+xml")
Location[] getLocations();

But what else has to be changed?

Community
  • 1
  • 1
joshi737
  • 869
  • 3
  • 12
  • 26

2 Answers2

2

You can use the Variant mechanisms of JAX-RS.

@GET
@Path("/locations/{id}")
@Produces(value = {"application/vnd.mycompany-v2+json", // current version
                   "application/vnd.mycompany-v1+json", // old version
                   MediaType.APPLICATION_JSON})         // fallback
public Response getLocation(@PathParam("id") Integer id,
                            @Context Request request) {
    MediaType vndTypeV1 = new MediaType("application", "vnd.mycompany-v1+json");
    MediaType vndTypeV2 = new MediaType("application", "vnd.mycompany-v2+json");
    Variant variant1 = new Variant(vndTypeV1, null, null);
    Variant variant2 = new Variant(vndTypeV2, null, null);
    Variant variantJson = new Variant(MediaType.APPLICATION_JSON_TYPE, null, null);
    List<Variant> variants = new ArrayList<Variant>();
    variants.add(variant1);
    variants.add(variant2);
    variants.add(variantJson);

    Variant selectedVariant = request.selectVariant(variants);

    Location location = someBackendService.getLocation(id);

    // Manipulate location according to which variant is the selectedVariant.
    // ...

    return Response.ok("{}")
        .header(HttpHeaders.CONTENT_TYPE, selectedVariant.getMediaType())
        .build();
}

See the Java EE 6 Tutorial, too.

Edit

There is no automatic way to marshal an entity according to the selected variant. This requires some manual work. For example:

String version = extractVersionFromVariant(selectedVariant);
if ("v1".equals(version)) {
    location.setSomeV1Propery("only in v1);
} else if ("v2".equals(version)) {
    location.setSomeV2Propery("only in v2);
}
return Response.ok(location)
               .header(HttpHeaders.CONTENT_TYPE, selectVariant.getMediaType())
               .build();

If the versions are different enough, I'd use a JAXB annotated class for each version. Each such class would then only contain those properties that are valid for this version. JAX-RS takes care to marshal them to JSON.

  • 1
    Thank you for your answer. But how does I handle such stuff: `@POST @Path("/locations") @Consumes({"application/vnd.mycompany-v2+json","application/vnd.mycompany-v1+json"}) void createLocation(Location location);` How does the marshaller use the correct Object-Version, for example I got an Location-Object for v1 and another one for v2 ? – joshi737 Oct 25 '12 at 09:47
  • 1
    Thank you again. The last two sentences of your answer sound interesting and are actually the point I'd like to achieve. If I have got the following REST Service and the client is asking for v2: `@GET @Path("/locations") @Produces({"application/vnd.mycompany-v1+xml", "application/vnd.mycompany-v2+xml"}) Location[] getLocations();` furthermore I got two Objekts in different packages like `entities.v1.Location` and `entities.v2.Location`. How can I tell the marshaller with the JAXB Annotations to use the Objekt `entities.v2.Location` cause the Client asked for that ?? – joshi737 Oct 25 '12 at 14:38
  • Just create an instance of the correct `Location` class in the `if` and return it in `ok()`. If the `Location` class has the proper JAXB annotations, JAX-RS should marshall it to JSON. –  Oct 25 '12 at 15:58
1

As far as I know you can't use JAX-RS to automatically route to different methods based on an http header.

You can read the header within you method (with @HeaderParam or HttpHeaders on the @context see here) and call the appropriate version

Arnon Rotem-Gal-Oz
  • 25,469
  • 3
  • 45
  • 68
  • 1
    Thank you for your answer. Actually I dont wanna use the version to route the request to different methods. I just want to use the version to produce Location Objects in the specified version. Maybe the Location of v2 has got one attribute more than v1...thats it – joshi737 Oct 24 '12 at 06:42
  • Then you can use what I mentioned above to figure out which version you need to return – Arnon Rotem-Gal-Oz Oct 24 '12 at 09:36