The demo shows a common use case for multiple request mappings and HATEOAS-assembled links with the infrastructural work behind it.
1. General
The following code snippet explains the idea behind connecting multiple @RequestMapping
annotations in conjunction with HATEOAS link generation.
1.1. Controller layout
@ResponseBody
@RequestMapping("/{pathVar:offer|angebot}") (1)
public Map<String, String> offer(@PathVariable String pathVar, @RequestParam Locale locale) {
validateCurrentPathVar(Page.OFFER, pathVar, locale); (2)
String listPathVar = PageMapping.valueOf(Page.LIST, locale).getPathVar(); (3)
Link listLink = linkTo(methodOn(HateoasController.class).list(listPathVar, locale)).withRel("list"); (4)
return ImmutableMap.of("currentPathVar", pathVar, "nextLink", listLink.getHref());
}
private void validateCurrentPathVar(Page page, String pathVar, Locale locale) {
if (!PageMapping.valueOf(page, locale).getPathVar().equals(pathVar))
throw new IllegalArgumentException(
pathVar + " is no valid path variable for page " + page + " and locale " + locale);
}
1 | Valid path variable expressions in regular expression style. URLs with both path variables will a priori
be possible, a locale-specific validation is not done in this step. HTTP 404 – Not found will be returned
if the path variable in the request does not match the regular expression. |
2 | Validates if the path variable of the current request matches with the locale-specific one for this endpoint.
If not, Spring exception handling will take over and throw HTTP 400 – Bad request (not shown here). |
3 | The locale-specific path variable for another endpoint is selected with respect of the known enum values in the PageMapping.java class (see below). |
4 | Spring HATEOAS assembles a strongly typed and per definition correct URL to another endpoint. |
1.2. Pages and Page mapping
public enum Page {
LIST, OFFER
}
@Getter
@ToString
@AllArgsConstructor
public enum PageMapping {
OFFER_EN(Page.OFFER, Locale.UK, "offer"),
OFFER_DE(Page.OFFER, Locale.GERMANY, "angebot"),
LIST_EN(Page.LIST, Locale.UK, "list"),
LIST_DE(Page.LIST, Locale.GERMANY, "liste");
private Page page;
private Locale locale;
private String pathVar;
public static PageMapping valueOf(Page page, Locale locale) {
return Arrays.stream(values())
.filter(mapping -> mapping.getPage() == page && mapping.getLocale().equals(locale))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No page mapping found for page " + page + " and locale " + locale));
}
}
2. Calling the endpoint
For generating links, Spring HATEOAS per default only takes the current request URI in account. However it obeys the X-Forwarded-Host header, e. g. for generating correct links for clients outside a load balancer. Examples of the two methods are shown below.
2.1. Without host header forwarding
Below you will find an example call – follow the links provided by its response to move around the controller endpoints.
You can call it manually via…
$ curl 'http://localhost:43127/angebot?locale=de_DE' -i -X GET
…regarding the following request parameter…
Parameter | Description |
---|---|
|
The locale for the current request, to determine the correct path variable during validation |
The request looks like…
GET /angebot?locale=de_DE HTTP/1.1
Host: localhost:43127
The response looks like…
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 12 Aug 2018 14:06:27 GMT
Content-Length: 94
{
"currentPathVar" : "angebot",
"nextLink" : "http://localhost:43127/liste?locale=de_DE"
}
…and has the following detailed semantics…
Path | Type | Description |
---|---|---|
|
|
The path variable used for the current request |
|
|
Locale-specific link to the other controller endpoint |
2.2. The X-Forwarded-Host feature
Watch the request header and the corresponding URL in the response! |
You can call it manually via…
$ curl 'http://localhost:43127/list?locale=en_GB' -i -X GET \
-H 'X-Forwarded-Host: test.de:9090'
…regarding the following request parameter…
Parameter | Description |
---|---|
|
The locale for the current request, to determine the correct path variable during validation |
…and request header…
Name | Description |
---|---|
|
The host for which the link shall be created. |
The request looks like…
GET /list?locale=en_GB HTTP/1.1
X-Forwarded-Host: test.de:9090
Host: localhost:43127
The response looks like…
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 12 Aug 2018 14:06:28 GMT
Content-Length: 88
{
"currentPathVar" : "list",
"nextLink" : "http://test.de:9090/offer?locale=en_GB"
}
…and has the following detailed semantics…
Path | Type | Description |
---|---|---|
|
|
The path variable used for the current request |
|
|
Locale-specific link to the other controller endpoint |