HATEOAS paths are invalid when using an API Gateway in a Spring Boot app
Asked Answered
A

5

9

I have two spring boot applications where one of them is acting as an API Gateway (as discussed here Spring Example). The other which is wired into the first one is exposing a profile service using spring-data-rest (spring-data-neo4j-rest).

The first application is starting on port 8080 and is using zuul to route requests to the second as follows:

zuul:
  routes:
    profiles:
      path: /profiles/**
      url: http://localhost:8083/profiles/  

This all works fine and requests to http://localhost:8080/profiles are being served from the second app. The problem though is that the HATEOAS links in the response are incorrect. The response from calling that second service are correct:

{
    "_links": {
        "self": {
            "href": "http://localhost:8083/profiles{?page,size,sort}",
            "templated": true
        },
        "search": {
            "href": "http://localhost:8083/profiles/search"
        }
    },
    "_embedded": {
        "profiles": [
            {
                "name": "Andrew Rutter",
                "_links": {
                    "self": {
                        "href": "http://localhost:8083/profiles/0"
                    }
                }
            },
            {
                "name": "Andrew Rutter",
                "_links": {
                    "self": {
                        "href": "http://localhost:8083/profiles/1"
                    }
                }
            }
        ]
    },
    "page": {
        "size": 20,
        "totalElements": 2,
        "totalPages": 1,
        "number": 0
    }
}

But when this comes back to my API Gateway, the links are being rewritten to

{
  "name": "Andrew Rutter",
  "_links": {
    "self": {
      "href": "http://localhost:8080/profiles/profiles/0"
    }
  }
}

Which is the gateway path alias plus the actual service base Uri. Am I missing a zuul option to disable that behavior and just leave the hateoas uri in place with a host adjustment. Or is there a way for my service behind the gateway to be wired to / rather then the default resource endpoint of /profiles (in this case) which would avoid the undesirable path being added in.

Thanks!

Alible answered 1/6, 2015 at 22:16 Comment(0)
P
6

Zuul or Spring-Cloud adds the "X-Forwarded-Host" header to all the forwarded requests, which Spring-hateoas respects and modifies the links appropriately. To quote from Spring-Cloud docs:

The X-Forwarded-Host header added to the forwarded requests by default. To turn it off set zuul.addProxyHeaders = false. The prefix path is stripped by default, and the request to the backend picks up a header "X-Forwarded-Prefix" ("/myusers" in the examples above).

You can try the recommended fix, which is to set the zuul.addProxyHeaders=false

Phlogistic answered 2/6, 2015 at 3:44 Comment(1)
Thanks for the suggestion. The result of setting that to false is that the links returned are the direct links to the backend service which is undesirable. I noticed that the spring-hateoas version being brought in was 0.16.0 so have tried changing that dependency up to current but no joy. I did change the api path to /xyz in the gateway to make things more clear. When I request a specific profile as /xzy/0, this is sent internally to /profiles/0 but the hateoas link in the response ends up as /xyz/profiles/0/profiles/0Alible
U
4

I had exactly the same problem. Change your config as follows:

zuul:
  routes:
    profiles:
      path: /profiles/**
      url: http://localhost:8083
      stripPrefix: false

This routes all requests going to the gateway matching "/profiles/**" to your back end server "http://localhost:8083" and leaves the prefix (in your case "/profiles" since that's what matched the route).

Unconcern answered 9/7, 2016 at 17:31 Comment(0)
B
0

Zuul forwards to the /profiles contextPath.

Try setting this as configuration:

zuul:
  routes:
    profiles:
      path: /profiles/**
      url: http://localhost:8083/
Barrator answered 2/6, 2015 at 19:52 Comment(2)
Thanks, I had tried that already and the context path from the original request is not passed across to the service. In the above example, I get a 404 that there is nothing available on / since Spring Data Rest exposes endpoints based on the model which is /profiles/* for this example but the gateway does not pass that across automagicallyAlible
Have you tried zuul: routes: profiles: path: /** url: localhost:8083/profiles ?Barrator
P
0

After struggling some time with the same problem, finally I've tried zuul.addProxyHeaders = true and it works! Links are not broken anymore.

Petrel answered 30/6, 2015 at 15:25 Comment(0)
S
0

In the demo app I used for SpringOne in my talk about Spring Data REST, I have the following configuration to both handle URI rewrites as well as adjust prefix headers set properly.

zuul:
  routes:
    api:
      path: /api/**
      serviceId: spring-a-gram-backend
      stripPrefix: false
    files:
      path: /files/**
      serviceId: spring-a-gram-mongodb-fileservice
      stripPrefix: false

See it in full at https://github.com/gregturn/spring-a-gram/blob/master/spring-a-gram-frontend/src/main/resources/application.yml#L23-L32

Stradivarius answered 24/9, 2015 at 2:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.