spring rest api versioning - spring

In my spring controller, I have 2 rest api methods. example: getUser, getRole
One client is accessing it by like "/api/v1".
Now I want to update one of the methods. i.e., getRole. So the new method/version will be "/api/v2".
But no change in methods of v1. i.e., "/api/v1".
How to handle the rest methods with both versions in the same project ?
I mean, getUser rest API should support both "/api/v1" and "/api/v2".
And getRole rest API should support both versions but different functionality (example: database changed, logic changed).
In simple words,
1. getUser will have 1 method which supports both versions.
2. getRole will have 2 methods for each versions.
Please help me here.

If you want to do use separate method for both version you can by defining different value in #RequestMapping
method 1
#RequestMapping(
value = "baseurl/v1/role",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
and
#RequestMapping(
value = "baseurl/v2/role",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
but if you want to handle it in same method you can using
method 2: use #PathVariable
#RequestMapping(
value = "baseurl/{version}/role",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public returnType methodName(#PathVariable("version") String version){
// code to check version
}
method 3: use #RequestHeader
#RequestMapping(
value = "baseurl/role",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public returnType methodName(#RequestHeader("version") String version){
// code to check version
}
But as you said you have different version of api's prefer to manage them in separate controller using #RequestMapping at class level
method 4:
#RestController
#RequestMapping("/v1")
public class anyController {
}
and
#RestController
#RequestMapping("/v2")
public class anyController {
}

It really depends on your use case and what all is changing. There are technically many different paths you can take, but I will describe two that sound like they would work for you.
Create path param for v1 and add conditional that checks to see if the path param is v2 and call different method.
Create new path for api/v2, add changes functionality and call v1 endpoint method.
This really gets into specifics and should probably be evaluated on which way you should take it, depending on how the existing code is implemented.

I would do the following approach
Define a version at the application level (Start with /v1) -- this version is changed only when you want to make bigger changes to your whole API and is usually stable.
Resources (roles, users) etc. should be versioned additionally using content negotiation via the headers
e.g.
GET /v1/users/503deb67-ff6e-4d1c-a225-408b910b7252/ HTTP/1.1
Accept: application/vnd.yourorg.users-v1+json
Here the client uses content negotiation for the specific resource she is interested in via the HTTP header
See https://www.mashery.com/blog/ultimate-solution-versioning-rest-apis-content-negotiation and http://blog.ploeh.dk/2015/06/22/rest-implies-content-negotiation/ for more details

There are many ways, but most likely you want to keep the code as decoupled as possible. This makes keeping the versions in separate classes a good option:
V1Controller.java:
#RestController
#RequestMapping("/api/v1")
public class V1Controller{
// version 1 api
}
V2Controller.java :
#RestController
#RequestMapping("/api/v2")
public class V2Controller{
// version 2 api
}
But for a even higher degree of decoupling you could have two different webapps deployed on the same Tomcat or Jetty server - version 1 under the context path "/api/v1" and version 2 on the context path "/api/v2". The webapps would simply be builds of different versions of the same code (or builds of different branches - if you're using git).

Related

RequestMapping discriminating by user in Spring

I have two controllers that handle the same resource. One for the regular web app and another one for portals (and in theory there could be more than one portal and more controllers).
#Controller
#RequestMapping(value = "/*/web/cases")
public class CasesDetailController extends BaseController<CasesForm> { ... }
#Controller
#RequestMapping(value = "/*/portal/cases")
public class CasesPortalDetailController extends BaseController<CasesForm> { ... }
In some controllers we just resolve a different view for the portal, but for some cases the controller logic is quite different. For example this one has many mappings in the portal that are not available in the app.
The problem comes with managing 2 different urls for the same resource. For example, if I wanna place a link for a case I have to calculate it in the server by checking if the current user is logged in the web or the portal instead of placing a constant base url and an uuid as parameter.
My question is if I can do something about the request mapping to decide which controller can I use. The best I can think of is using a parameter:
#RequestMapping(value = "/*/cases") // Web
#RequestMapping(value = "/*/cases", params = "device=portal") // Portal
But I would like to avoid sending that info in the url when I have it available in the user (we use a custom user class that extends the base user from spring and it contains the device). Is there any way to check that in the request mapping? Something like this:
#RequestMapping(value = "/*/cases", magicFunctionality=getLoggedUser().getDevice()="web")
Probably nothing like this exists (at least I found nothing in the manual) but any ideas of how to face this problem would be wellcome.
Thanks in advance.
Gonzalo.

How to list twice the same GET method in Swagger integrated in Spring Boot

I have a #RestController with a GET method to list all the instances of a Resource R
I want swagger-ui to list thrice this GET method like this:
findRByFoo
findRByBar
findRByFooAndBar
that corresponds to the following GET petitions:
/resources?foo=myFoo
/resources?bar=myBar
/resources?foo=myFoo&bar=myBar
so that the clients of my API Rest don't have to guess that they can search by foo or bar and can simply look at the swagger-ui (version 2.9.2) and use those API calls
However, given that the three methods are at the /resources path, swagger simply lists one of them and only one.
The question is, How may I list the three API calls?
Edit: It seems to be a limitation of Swagger 2 Unable to add multiple operations on same path in swagger rest API documentation #1378, so let me rephrase the question as:
How may I circumvent this limitation?
Does it mean that the design of my API Rest is not as Rest as should be?
I rewrited the RController to have a single GET
#RestController("/path-to-r")
public class RController
{
#GetMapping
public List<R> findR(Optional<String> foo, Optional<String> bar)
{
....
}
}
And added genericModelSubstitutes as explained in Java 8 Optional #RequestParam lost from the swagger-ui #1848
The final result is such that when the clients of my API click in the combo they can see the two optional parameters to include in the GET petition

How to get enunciate to ignore duplicate REST paths?

We have several methods in a spring REST API that designed to handle requests that fit our API exactly, or allow for a trailing slash. We use this annotation:
#RequestMapping(value = {"", "/"}, produces = {"application/json"}, method = RequestMethod.POST)
#ResponseBody
public ClassName ...
When enunciate / swagger docs are generated, they have two items generated ("domain.com/api" and "domain.com/api/"), I am wondering if there is a way to tell enunciate to only show one?
Consider options for handling the mapping of "/api" and "/api/" at a deeper level instead of defining two endpoints in your Java class. I am sure there are many ways to map these as aliases, such as in web.xml.
Enunciate has the ability to ignore an entire class using the ignore annotation but this would require you to restructure your class definitions (in a beautiful and reusable way) so the two endpoints are handled by different classes, and then one you can annotate one with #Ignore.

Spring ROO - To get value in java class

I have created Spring ROO application using below link in Eclipse.
http://www.cubrid.org/blog/dev-platform/spring-roo-fast-java-application-development-tool/
In this i am having a controller named BookController which is having #RequestMapping("/book"). This works fine but When i want to get data from textfield to java class i am not getting by moving this RequestMapping to method level.
The actionURL of page is = /SPringDemo/book
When i am moving this to method level like this
#RequestMapping(value = "/book")
public String gettingData(Book book) {
System.out.println("Book is = " +book.getName());
return null;
}
This is not performing opeartion
Spring web mvc try to match request with methods. This include parameters required. In your request definition you ask for a Book instance but, if Spring can locate any parameter or model attribute which make matching to definition, your method will not be invoked.
Check this example which is similar to your case.
Good luck!

Spring 3.1.RC1 and PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE

Posted in spring forum with no response.
I have the following code snippet (from here), which is part of my pet project.
#Controller
#RequestMapping("/browse")
public class MediaBrowser {
...
#RequestMapping("/**")
public final ModelAndView listContents(final HttpServletRequest request) {
String folder = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
...
}
I access the following url:
http://localhost:8080/myapp/browse
In spring 3.0.6.RELEASE, I got the folder variable as null, which is the expected value.
In spring 3.1.RC1, the folder variable is /browse.
Is this a bug or has something changed in spring-3.1?
As skaffman said, you probably shouldn't use PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE. Take a look at How to match a Spring #RequestMapping having a #pathVariable containing "/"? for an example of using AntPathMatcher to accomplish what you are trying
This looks very much like an internal implementation detail of the framework, one that you should not be relying on.
The javadoc for PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE says:
Note: This attribute is not required to be supported by all HandlerMapping implementations. URL-based HandlerMappings will typically support it, but handlers should not necessarily expect this request attribute to be present in all scenarios.
I wouldn't be surprised if the behaviour changed slightly between 3.0 and 3.1.

Resources