In the below code snippet, I am trying to match my request against a custom predicate. Upon the predicate being evaluated as false, I would like to send back a custom status code (403 Forbidden in the below snippet) instead of the default 404 that is being sent on predicate failure. Here's what I've tried.
RouteLocator
@Bean
public RouteLocator customRoutesLocator(RouteLocatorBuilder builder
AuthenticationRoutePredicateFactory arpf) {
return builder.routes()
.route("id1", r ->r.path("/app1/**")
.uri("lb://id1")
.predicate(arpf.apply(new Config()))).build();
}
AuthenticationRoutePredicateFactory
public class AuthenticationRoutePredicateFactory
extends AbstractRoutePredicateFactory<AuthenticationRoutePredicateFactory.Config> {
public AuthenticationRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return (ServerWebExchange t) -> {
try {
Boolean isRequestAuthenticated = checkAuthenticated();
return isRequestAuthenticated;
}
} catch (HttpClientErrorException e) {
//This status code does not carried forward and 404 is displayed instead.
t.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return false;
}
};
}
@Validated
public static class Config {
public Config() {
}
}
private Boolean checkAuthenticated() {
// Some sample logic that makes a REST call and returns TRUE/FALSE/HttpClientErrorException
//Not shown here for simplicity.
return true;
}
}
When the predicate returns as true, the request is forwarded to the URI. However, on false evaluation 404 is displayed, I require 403 to be displayed (On HttpClientErrorException ). Is this the right way to expect a response with custom status code?. Additionally, I also read on implementing custom webfilters for a given route that can possibly modify the response object before forwarding the request. Is there a way to invoke a filter on predicate failure in that case?
As a newbie to spring cloud gateway, I chose the wrong direction towards approaching this problem.
Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. Predicates aid the Gateway Handler Mapping in determining if that request matches the route and can only return either a true or false value.
Once the request matches the route, The handler runs the request through a filter chain that is specific to the request. This is where "pre" or "post" requests can be applied before forwarding a request.
Therefore In order to send a custom response status code before conditionally forwarding the request, One must write a custom "pre" filter and this can be achieved as below. (Setting 403 status code)
AuthenticationGatewayFilterFactory
@Component
public class AuthenticationGatewayFilterFactory
extends AbstractGatewayFilterFactory<AuthenticationGatewayFilterFactory.Config> {
public AuthenticationGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
try {
if(isRequestAuthenticated) {
return chain.filter(exchange);
}
else {
exchange.getResponse().setStatusCode(403); // Any status code can be set here.
return exchange.getResponse().complete();
}
} catch (HttpClientErrorException e) {
exchange.getResponse().setStatusCode(403); // Any status code can be set here.
return exchange.getResponse().complete();
}
};
}
public static class Config {
}
private Boolean isRequestAuthenticated(String authToken) {
// Some sample logic that makes a REST call and returns TRUE/FALSE/HttpClientErrorException
//Not shown here for simplicity.
return true;
}
RouteLocator
@Bean
public RouteLocator customRoutesLocator(RouteLocatorBuilder builder
AuthenticationGatewayFilterFactory agff) {
return builder.routes()
.route("id1", r ->r.path("/app1/**")
.uri("lb://id1")
.filter(agff.apply(new Config()))).build();
}