Almost all APIs are dealing with different release versions. Often you see this kind of versioning:
But I haven't found a source discribing how to organize them in a Spring stack. I guess having a /v1
prefix like @RequestMapping("/v1/questions")
on every Controller is not the best approach.
Imagine there is a @Service
layer of just the current release (in our case V2).
Our Service should handle the requests of V1 and V2. The only change was that V2 added a new field on a question entity (that means a V1 Question could be easily converted to a V2 question).
Now the Questions are:
~.web.* @Controller
from a java package point of view?~.web.* @Controller
that they are aware of their versions? In manner of the RequestMapping
? Or is it possible to configure them with a context:component-scan in a V1 java package?An example could look like this (i added the packages everywhere):
// on V1 Question look like this:
public class project.domain.Question
{
private String question;
}
// on v2 Question looks like this:
public class project.domain.Question
{
private String question;
private Date creationDate;
}
@Service
public class project.service.QuestionService
{
public long create(Question q) {...};
public Question read(long id) {...};
public void remove(long id) {...};
public void update(Question qd) {...};
}
@Controller
@RequestMapping("/v2/question")
public class project.web.v2.QuestionController
{
@Autowired
project.service.QuestionService questionService;
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public long create(Question q)
{
return questionService.create(q);
}
}
@Controller
@RequestMapping("/v1/question")
public class project.web.v1.QuestionController
{
@Autowired
project.service.QuestionService questionService;
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public long create(Question q)
{
// this will not work, because the v1 haven't had the 'creationDate' field.
return questionService.create(q);
}
}
Through a URI path – you include the version number in the URL path of the endpoint, for example, /api/v1/persons. Through query parameters – you pass the version number as a query parameter with a specified name, for example, /api/persons? version=1.
Versioning a REST API is a complex problem. First, let's identify some high-level approaches to versioning:
With that in mind, let's consider some goals: (straight out of API Evolution)
Next, let's consider some possible changes to the API:
The language should be designed explicitly with forward compatibility in mind and Clients should ignore information they don't understand.
As such, adding information to a Representation of a Resource is not an incompatible change.
Such extensions/changes to the language can leverage the Accept header and Content Negotiation - Representations are versioned using custom vendor MIME media type. These articles goes more in depth on this: API Versioning, Versioning REST Web Services.
As such, this does represent an incompatible change for the Client - which will have to request the new Representation and understand the new semantics, but the URI space will remain stable and will not be affected.
These are changes in the meaning of the Resources and the relations between them. In the case, we can look at changing the mapping between the Resources and the URI structure. However, that still doesn't necessarily mean using a version indicator in the URI.
The REST API should adhere to the HATEOAS constraint - most of the URIs should be DISCOVERED by Clients, not hardcoded. Changing such an URI should not be considered an incompatible change - the new URI can replace the old one and Clients will be able to re-discover the URI and still function.
For such sweeping changes, version indicators in the URI are the last resort solution.
DispatcherServlets
may make sense as it allows for a clean separation of the URI spaces@RequestMapping
new attributes - produces
and consumes
are also helpfulSome other very useful resources:
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With