简体   繁体   中英

REST Api Implementation Using Spring

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM