I am working on Spring Cloud and using the sample project by Josh Long at
Bootiful Microservice by Josh Long
There is an API gateway reservation-client which consumes data from the service reservation-service which provides a HATEOAS response, which then is converted to a simple JSON response.
@RestController
@RequestMapping("/reservations")
class ReservationApiGateway {
Method:
@HystrixCommand(fallbackMethod = "fallback")
@RequestMapping(method = RequestMethod.GET, value = "/names")
public Collection<String> names() {
return this.reservationReader
.read()
.getContent()
.stream()
.map(Reservation::getReservationName)
.collect(Collectors.toList());
}
I modify it to forward me the HATEOAS response like this.
@HystrixCommand(fallbackMethod = "fallback")
@RequestMapping(method = RequestMethod.GET, value = "/names")
public Resources<Resource<Reservation>> names() {
return this.reservationReader
.read();
}
This is giving me a HATEAOS response, but the links are all from the reservation-service - .
"_links" : {
"self" : {
"href" : "**http://192.168.0.3:7000/reservations/1**"
},
"reservation" : {
"href" : "http://192.168.0.3:7000/reservations/1"
}
}
How do I make sure Feign updates the links to the server and port of the API Gateway? - http://192.168.0.3:9999/reservations/1
Same response from reservation-client(same as reservation-service):
{
"_embedded" : {
"reservations" : [ {
"reservationName" : "Josh",
"_links" : {
"self" : {
"href" : "http://192.168.0.3:7000/reservations/1"
},
"reservation" : {
"href" : "http://192.168.0.3:7000/reservations/1"
}
}
}, {
"reservationName" : "Dr. Johnson",
"_links" : {
"self" : {
"href" : "http://192.168.0.3:7000/reservations/2"
},
"reservation" : {
"href" : "http://192.168.0.3:7000/reservations/2"
}
}
}, {
"reservationName" : "Dr. Syer",
"_links" : {
"self" : {
"href" : "http://192.168.0.3:7000/reservations/3"
},
"reservation" : {
"href" : "http://192.168.0.3:7000/reservations/3"
}
}
}, {
"reservationName" : "Dr. Pollack",
"_links" : {
"self" : {
"href" : "http://192.168.0.3:7000/reservations/4"
},
"reservation" : {
"href" : "http://192.168.0.3:7000/reservations/4"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://192.168.0.3:7000/reservations{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://192.168.0.3:7000/profile/reservations"
},
"search" : {
"href" : "http://192.168.0.3:7000/reservations/search"
}
}
}
I figured it out.
The solution was in the X-Forwarded-Host http header. X-Forwarded-Host essentially tells Spring that any HATEOS response which has this header, the host and port information in the links should be updated to what is mentioned in the X-Forwarded-Host http header.
So, in the API Gateway reservation-client code, I added this snippet which intercepts Feign's call to the backend service reservation-service and adds the http header to the request.
@Component
class LanguageRequestInterceptor implements RequestInterceptor {
private static final String X_FORWARDED_HOST = "X-Forwarded-Host";
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return;
}
HttpServletRequest request = requestAttributes.getRequest();
if (request == null) {
return;
}
requestTemplate.header(X_FORWARDED_HOST, "localhost:9999");
}
}
Now, all the HATOES response has the host and port information of the API Gateway, not the backend HATEOS service.