6

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.

Jonathan
  • 377
  • 5
  • 20

2 Answers2

1

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.

Stony
  • 3,541
  • 3
  • 17
  • 23
  • +1 I'm only not so fond of the `I` prefix for interfaces. A client shouldn't know it's using an interface or an implementation. – Bart May 22 '13 at 14:36
  • and what about the JSON objects? they are generated automatically according to the relevant classes. Do I need to duplicate these classes? – Jonathan May 24 '13 at 09:41
  • i don't think we have to duplicated these classes. ("these classes" means entry/view classes, right?). As I thought, the v1/v2 will contain some business logic, the return should be same. If v1 don't support some method, just throw exception. – Stony May 25 '13 at 01:15
0

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.

CodeChimp
  • 8,016
  • 5
  • 41
  • 79
  • **a)** There is no need for regular expressions. So why use it? **b)** Duplicating code all over the place will not be very manageable when to number of api versions grow. **c)** You can't simply dump one version for another. Clients are using those api's. – Bart May 22 '13 at 14:38
  • a) The regex can be used to to aid in matching the request URL. That was what the OP was asking for b) No one said to duplicate code. He is creating a new API while leaving the old there. That would be new code, right? I don't think anything I mentioned would require and "duplicate code" c) No one said dump the v1. I said "you can use regex OR you can have another method for v2 in your controller OR you could change your version to a path var, have one method that then switches to the correct underlying method for the version requested" – CodeChimp May 22 '13 at 16:57
  • By the way, I believe what I suggested in the last was similar to what @StonyZhang suggested, which is use the version as a path variable to determine which version to call. I had assumed the OP was using standard OOP and MVC practices, and a "service layer" would be there somewhere. – CodeChimp May 22 '13 at 17:00