Reputation: 417
One of the best practices of writing RESTFul api application, is to add versioning. for example:
http://my-server/api/v1/getData
http://my-server/api/v2/getData
Our application exposes REST api using the Spring framework. We mark a class as a Controller, use RequestMapping annotation to map URL to a function, and add some objects that are translated to / from json objects.
For example:
@RequestMapping(method = RequestMethod.POST, value = "/api/v1/getData")
public @ResponseBody ResponseDataDTO getData(@RequestBody OperationsDetailsTDO details) {...}
Now, we want to provide the second version of the API. Around 2/3 of the functions remain the same, and 1/3 are changing. The changes are in both logic and the JSON objects.
I wonder how to design the code. I think that this kind of code is hard to manage:
@RequestMapping(method = RequestMethod.POST, value = "/api/{version-var}/getData")
public @ResponseBody ResponseDataDTO createReleaseFromTemplate(@PathVariable("version-var") Integer version, @RequestBody OperationsDetailsTDO details) {
if (version == 1)
{
doForVersion1();
}
else if (version == 2)
{
doForVersion2();
}
}
It will be hard to manage, since in each function there will be different branching. Just to demonstrate the problem, if I have an automatic tool that generates documentation - it won't be able to understand what is the API.
Second, I wonder what should I do with the classes that are binded to JSON object. Do I need to duplicate all of these classes, for minor changes?
Thx.
Upvotes: 6
Views: 2383
Reputation: 3624
I agree with you on passing version as a parameter, just like
@RequestMapping(method = RequestMethod.POST, value = "/api/{version-var}/getData")
But I don't think it's a good idea to add lots of branches, We should extract all methods in the resource class to a business interface, such as,
private IDataRetrieve dataRetriever;
@RequestMapping(method = RequestMethod.POST, value = "/api/{version-var}/getData")
public @ResponseBody ResponseDataDTO createReleaseFromTemplate(@PathVariable("version-var") Integer version, @RequestBody OperationsDetailsTDO details) {
dataRetiever = DataRetrieverFactory.getDataTrieverByVersion(version); //TODO, create a factory to get DataRetriever
return dataRetiever.getData();
}
And then you need two classed to implement IDataRetriver, (one for V1, another for v2); of cause, to avoid duplicated code, you can add a abstract class for V1 and V2, and use Template Patern to remove the duplicated code.
Upvotes: 1
Reputation: 8154
You can use regular expressions in your RequestMapping if you use a path variable for the version (see here for example). Or, you could create a new method that simply calls the old one but has the newer RequestMapping for v2. Or, create a generic method that grabs the version and calls out to the proper method that actual handles that version of the request. Bonus for the last method: You could grab the version and send the user to a specific error if the version they send is not supported, for instance if they send /app/v10/whatever.
Upvotes: 0