Search code examples
spring-bootnetflix-zuul

Dynamically re-route all Zuul proxied requests to single URL


I'm trying to forward all requests to my API to a single endpoint based upon some condition.

The Gateway app runs on port 8080

I've created the following filter:

public class OutagePeriodFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "route";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return isOutagePeriod();
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();


        // if outage - redirect everyone to http://localhost:8082/outage
        try {
            String url = UriComponentsBuilder.fromHttpUrl("http://localhost:8082").path("/outage").build()
                                     .toUriString();
            ctx.setRouteHost(new URL(url));
        } catch(MalformedURLException mue) {
            log.error("Cannot forward to outage period website");
        }
        return null;
    }


    private boolean isOutagePeriod() {
        // returns true if outage
    }
}

However after making a request to http://localhost:8080/alerts/public my API logs show:

2017-03-03 16:11:30.735 EST 0037 DEBUG                    o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/outage/alerts/public'; against '/beans/'

For some reason it appends the original PATH to the redirect PATH resulting in a request to /outage/alerts/public which doesn't exist. I want to make a request to just /outage.

Putting a breakpoint in my filter just as the ctx.setRouteHost() is called shows that correct URL (http://10.50.36.43:8082/outage/).

My application.properties:

zuul.routes.api.path=/api/** zuul.routes.api.url=http://localhost:8082/


Solution

  • First, use PRE_TYPE instead of ROUTE_TYPE (aka "route")

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    

    Second, specify correct filterOrder

    @Override
    public int filterOrder() {
        return FilterConstants.SEND_FORWARD_FILTER_ORDER;
    }
    

    Third, you need to remove URI containing "/alerts/public", which is appended to the routeHost by zuul

    @Override
    public Object run() {
    
        ctx.set("requestURI", "");
    
        // your code
    
        return null;
    }
    

    Finally, override forwarding url in the code (//your code). And you have to specify at least one route (cause zuul filters don't work if there are no routes) to proxy "not outaged" requests to the same host 8080 like this

    zuul.routes.api.path=/api/**
    zuul.routes.api.url=http://localhost:8080/api
    

    IMHO, zuul is not a very convenient way to do this.