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

locale

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

currentPathVar

String

The path variable used for the current request

nextLink

String

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

locale

The locale for the current request, to determine the correct path variable during validation

…and request header…

Name Description

X-Forwarded-Host

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

currentPathVar

String

The path variable used for the current request

nextLink

String

Locale-specific link to the other controller endpoint