Best way to handle JAX-RS REST API URI versioning
I did my search first in stackoverflow & I was not able to find out any answers related to my question. All I can find was questions related to REST uri design.
My question in on the backend side. Suppose we have two different version of REST uri's
http://api.abc.com/rest/v1/products
http://api.abc.com/rest/v2/products
What is the best approach to follow on the backend side (server side code) for proper routing, manageability & reuse of the existing classes across these two set of api's based on version?
I have thought of approach to define resource classes with different @Path annotations for e.g. have a package for v1 & v2 separately & in ProductsResource class of that package, define
package com.abc.api.rest.v1.products;
@Path("/rest/v1/products")
public class ProductsResource {...}
package com.abc.api.rest.v2.products;
@Path("/rest/v2/products")
public class ProductsResource {...}
& then have the implementation logic based on the versions. The problems with this approach is when we are only changing one particular resource api from the set of api's, we have to copy other classes to the v2 package also. Can we avoid it?
How about to write a custom annotation say @Version & have values of the versions it supports? Now whether it is v1 or v2, both request will go to same resource class.
Say for e.g.
package com.abc.api.rest.products;
@Path("/rest/{version: [0-9]+}/products")
@Version(1,2)
public class ProductsResource {...}
UPDATE:
There was a API versioning suggestion by Jarrod to handle version in headers. That's also one way to do it however, I am looking forward for best practices to use when we are following URI based versioning
The problem with putting it in the URL is that the URL is supposed to represent a resource by location. An API Version is not a location and it not part of the identifier of the resource.
Sticking /v2/
in the URL breaks all existing links that came before.
There is one correct way to specify API versioning:
Put it in the mime-type for the Accept:
header that you want. Something like Accept: application/myapp.2.0.1+json
Chain of Responsiblity pattern goes well here especially if there will be significant number of API versions that are different enough to have to have their own handler, that way methods don't get out of hand.
This blog post has an example of what is considered the by some to be the correct approach, i.e. not having the version in the URI: http://codebias.blogspot.ca/2014/03/versioning-rest-apis-with-custom-accept.html
In short, it leverages JAX-RS @Consume
annotation to associate the request for a particular version to a specific implementation, like:
@Consumes({"application/vnd.blog.v1+xml", "application/vnd.blog.v1+json"})
I was just wondering why not have a subclass of ProductService called
@Path(/v2/ProductService)
ProductServiceV2 extends ProductService {
}
@Path(/v1/ProductService)
class ProductService{
}
and only override whatever is changed in v2. Everything unchanged will work the same as in v1/ProductService.
This defintely leads to more # of classes but is one easier way of coding for only whatever is changing in the new version of api and reverting to the old version without duplicating code.