I am new to spring-cloud-gateway and I can't answer the question if I solved my problem the intended way. I hope someone can point me into the right direction, give advice or provide some exemplary sample code.
Requirement: An incoming request at my spring-cloud-gateway service shall be forwarded to the correct backend service (there are X of such backend services, each responsible for a specific task). The request itself does not contain enough information to decide which backend service to route to.
An additional REST-service exists that maps an arbitrary URL-to-responsible-backend-service-name. The response format is some small JSON, containing the name of the backend-service to forward to.
What would be the easiest/best/smartest/intended solution to implement this functionality with spring-cloud-gateway?
I tried implementing a GatewayFilter that first calls the mapping-service and depending on the result set GATEWAY_REQUEST_URL_ATTRfor the exchange.
This works ok. But I have additional questions.
Is it possible to omit the
.uri("not://needed"))part in the route setup?Why does the order value need to be higher than 9999? (see code example)
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().route(r -> r
.alwaysTrue()
.filters(f -> f.filter(new CustomRequestFilter()))
.uri("not://needed")) // how to omit ?
.build();
}
public static class CustomRequestFilter implements GatewayFilter, Ordered {
@Override
public int getOrder() {
return 10000; // why order > 9999 ? (below 10000 does not work)
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return getBackendServiceMappingResult(exchange.getRequest().getURI().toString()) //async call to REST service mapping URL->backend service name
.flatMap(mappingResult -> {
URI uri = mapServiceNameToBackendService(mappingResult.getServiceName());
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri);
return chain.filter(exchange);
});
}
private URI mapServiceNameToBackendService(String serviceName) {
try {
switch (serviceName) {
case "serviceA": return new URI("http://httpbin.org:80/get");
case "serviceB": return new URI("https://someotherhost:443/");
}
} catch (URISyntaxException e) {
//ignore
}
return null;
}
}
static class MappingResult {
String serviceName;
public String getServiceName() {
return serviceName;
}
}
static Mono<MappingResult> getBackendServiceMappingResult(String uri) {
WebClient client = WebClient.create("http://localhost:8080");
return client.get().uri(uriBuilder -> uriBuilder.path("/mapping").queryParam("uri", uri).build()).retrieve().bodyToMono(MappingResult.class);
}
}
Is there a better approach (with spring-cloud-gateway) to solve the requirement?