Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamically re-route all Zuul proxied requests to single URL #1754

Closed
ashleyconnor opened this issue Mar 3, 2017 · 14 comments
Closed

Dynamically re-route all Zuul proxied requests to single URL #1754

ashleyconnor opened this issue Mar 3, 2017 · 14 comments
Labels

Comments

@ashleyconnor
Copy link

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/
@spencergibb
Copy link
Member

spencergibb commented Mar 3, 2017

It should be a "pre" filter and it should run before PreDecorationFilter. I think setting the requestURI field in RequestContext will help. Otherwise spring cloud appends the original path.

@ashleyconnor
Copy link
Author

The request still makes it to the original destination.

New code:

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

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

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

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

Breakpoint confirming it's run before PreDecorationFilter.

2017-03-06 11_49_23-virtualbox

@spencergibb
Copy link
Member

What happens in ProxyRequestHelper.buildZuulRequestURI()?

@spencergibb
Copy link
Member

My guess is that predecorationfilter is still running later. Maybe it needs to happen after predecoration.

@ashleyconnor
Copy link
Author

Changing the filter to run after PreDecorationFilter and setting ctx.set("requestURI", url) worked.

I removed ctx.setRouteHost(new URL(url)); as it doesn't appear to be required.

Final code:

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

        String url = UriComponentsBuilder.fromHttpUrl("http://localhost:8082").path("/outage").build()
                                     .toUriString();
        ctx.set("requestURI", url);
        return null;
    }

@oterrien
Copy link

Strange because I had the same need and I succeeded thanks to this post but with ctx.setRouteHost

@GhanshyamRawat-eGov
Copy link

This solution is not working for me, please help me:
My pre filter class code:

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

	System.out.println(
			"Request Method : " + request.getMethod() + " Request URL : " + request.getRequestURL().toString());
	String body = readRequestBody(request);
	String url1 = UriComponentsBuilder.fromHttpUrl("http://localhost:8082").path("/wf-service").path("/_create")
			.build().toUriString();
	try {
		ctx.setRouteHost(new URL(url1));
		ctx.set("requestURI", url1);
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	requestParser.setReqAsMap(body);
	if (requestParser.hasRequestInfo()) {
		System.out.println("has req info");
	}
	return null;
}
@Override
public boolean shouldFilter() {
	return true;
}
@Override
public int filterOrder() {
	return 1;
}
@Override
public String filterType() {
	return "pre";
}

application.properties

zuul.routes.wf.path=/wf-service/**
zuul.routes.wf.stripPrefix=false
zuul.routes.wf.url=http://localhost:8082/

@qcastel
Copy link

qcastel commented Nov 14, 2017

The previous answers seems to indicate that the filter needs to be triggered after the PreDecoration.

Have you try to change the filterOrder to:

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

@GhanshyamRawat-eGov
Copy link

i change this also now it appending complete url twise
{
"timestamp": 1510720356916,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/wf-service/_createhttp:/localhost:8082/wf-service/_create"
}

@san13692
Copy link

san13692 commented Mar 2, 2018

Worked for me too by changing the FilterOrder to 6. I was trying to prevent routing from happening.And so removed "routeHost" from the RequestContext in the Pre Filter. Thanks @spencergibb

@yuvalbar84
Copy link

Hi all, trying to achieve the same thing,
but the ctx.setRouteHost() actually sends a 302 (Redirect) to my browser, and therefore it is not proxied by Zuul.
Any suggestion would be highly appreciated.

@aswinde03032008
Copy link

This solution is not working for me, please help me:
My pre filter class code:

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

	System.out.println(
			"Request Method : " + request.getMethod() + " Request URL : " + request.getRequestURL().toString());
	String body = readRequestBody(request);
	String url1 = UriComponentsBuilder.fromHttpUrl("http://localhost:8082").path("/wf-service").path("/_create")
			.build().toUriString();
	try {
		ctx.setRouteHost(new URL(url1));
		ctx.set("requestURI", url1);
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	requestParser.setReqAsMap(body);
	if (requestParser.hasRequestInfo()) {
		System.out.println("has req info");
	}
	return null;
}
@Override
public boolean shouldFilter() {
	return true;
}
@Override
public int filterOrder() {
	return 1;
}
@Override
public String filterType() {
	return "pre";
}

application.properties

zuul.routes.wf.path=/wf-service/**
zuul.routes.wf.stripPrefix=false
zuul.routes.wf.url=http://localhost:8082/

GhanshyamRawat -> is resolved now?
I am also getting same issue.

@aswinde03032008
Copy link

Changing the filter to run after PreDecorationFilter and setting ctx.set("requestURI", url) worked.

I removed ctx.setRouteHost(new URL(url)); as it doesn't appear to be required.

Final code:

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

        String url = UriComponentsBuilder.fromHttpUrl("http://localhost:8082").path("/outage").build()
                                     .toUriString();
        ctx.set("requestURI", url);
        return null;
    }

I did the same but it did not work
Here is my code :
bootstrao.yml:
zuul.routes.cp.path: /navxcp/**
zuul.routes.cp.service-id: navxcp

zuul.routes.web.path: /navx/**
zuul.routes.web.service-id: navxweb

Prefilter:
requestUri = requestUri.replace("navx", "navxcp");
ctx.set(FilterConstants.REQUEST_URI_KEY,requestUri );
log.info("--- request-uri has been modified -> {}",requestUri);

actually I am hitting http://host:port/gateway/navx/getData
Here getData endpoint is available on navxcp microservice
So I am trying to change the requestURI in pre-filter so that it should be routed to navxcp

@sail-y
Copy link

sail-y commented Dec 27, 2018

I found one way to do this.

here is my zuul config:

zuul:
  routes:
    sevice-demo-b:
      path: /b/**
      url: http://localhost:5010
    sevice-demo-a: /a/**

The filter should be pre and before PRE_DECORATION_FILTER

the requestUri attribute can change you uri after preDecorationFilter choose the route, but if you want to change the route, you need use this code request.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, url);

here is the code

public class ChangeUriFilter extends ZuulFilter {

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

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        final String uri = request.getRequestURI();

        return uri.contains("/helloA");

    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        final HttpServletRequest request = ctx.getRequest();
        
        String uri = "/b/helloB"

        // change uri 
        request.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, url);
        
        return null;
    }
}

when you request 'http://host:port/a/helloA', it will forward to http://host:port/b/helloB

😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants