spring-cloud / spring-cloud-netflix

Integration with Netflix OSS components
http://cloud.spring.io/spring-cloud-netflix/
Apache License 2.0
4.87k stars 2.44k forks source link

Zuul - Request/Response transformation #1733

Closed stiyyagura closed 7 years ago

stiyyagura commented 7 years ago

I am trying to transform the original request by writing custom wrapper extending HttpServletRequestWrapper and adding additional attribute in ZuulFilter 1. But my origin service is not receiving this attribute but in Zuul filter 2 i can access this attribute. Do i need to do anything else to handle this use case. I might need to do the same thing for response as well. Please suggest best way to do request/response transformations.

public class BaseRequestWrapper extends HttpServletRequestWrapper{

    public BaseRequestWrapper(HttpServletRequest request, String str) {
        super(request);
        request.setAttribute("att1", str);
    }
}
ryanjbaxter commented 7 years ago

Why are you not using a ZuulFilter to do what you need?

spencergibb commented 7 years ago

Besides, Servlet Request attributes are not part of HTTP and do not get forwarded. You would need to use a header.

stiyyagura commented 7 years ago

@spencergibb very true i was aware of that just tried to give one example of adding more to incomeing request at Zuul level but my objective is to have a better control of the incoming request and add/transform the request/response in to origin service understandable contract. Can you suggest a way to do it? @ryanjbaxter that's exactly i wanted to do but didn't get a complete picture on how to get the request (headers,body,etc..) information and transform it. Any advice is really helps.

ryanjbaxter commented 7 years ago

There are plenty of examples that you can look at here https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/pre

spencergibb commented 7 years ago

Also see https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#zuul-developer-guide

stiyyagura commented 7 years ago

@ryanjbaxter @spencergibb I have tried like below,

requestField = ReflectionUtils.findField(HttpServletRequestWrapper.class, "req", HttpServletRequest.class);
HttpServletRequest wrapped = (HttpServletRequest) ReflectionUtils.getField(this.requestField, request);
XYZRequestWrapper wrapper = new XYZRequestWrapper(wrapped );
inputStream = wrapper .getInputStream(); -- This is always giving 
String str = IOUtils.toString(inputStream, StandardCharsets.UTF_8);

But this is not giving me any byte array/input stream where as if i pass the RequestContext(zuul).getRequest diredtlly to the above wrapper i am getting the input stream/byte array. Not sure why it is behaving as per the Zuul orginal request is in "req" element!!!

Please let me know if it safe to do like this...below

RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
        XYZRequestWrapper wrapper = null;
        try {
            wrapper = new XYZRequestWrapper (request);
            InputStream inputStream = wrapper.getInputStream();
            String str = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            LOGGER.info("STRING: " + str);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
spencergibb commented 7 years ago

Using a wrapper isn't the right way to access the input stream. See https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/RibbonRoutingFilter.java#L196-L209 for how we access the input stream.

It is in the RequestContext with the key requestEntity.

stiyyagura commented 7 years ago

@spencergibb I see the below code in there. Even here we are getting the InputStream from the request how is this different from my wrapper?? Just an fyi My wrapper i am calling in "pre" filters..something similar to FormBodyRequestWrapper (this is giving only query params as stream) but i need body of the request as well since i am trying to get the same from request.getInputStream().

Please let me know if i need to upload the classes..i can do that.

protected InputStream getRequestBody(HttpServletRequest request) {
        InputStream requestEntity = null;
        try {
            requestEntity = (InputStream) RequestContext.getCurrentContext()
                    .get("requestEntity");
            if (requestEntity == null) {
                requestEntity = request.getInputStream();
            }
        }
        catch (IOException ex) {
            log.error("Error during getRequestBody", ex);
        }
        return requestEntity;
    }
scaryPonens commented 7 years ago

How can I access original Request attributes particularly the X509 certificate chain from a zuul filter?

ryanjbaxter commented 7 years ago

@scaryPonens please open a separate issue

scaryPonens commented 7 years ago

I thought the question was very similar and relevant to the thread. I found my answer reviewing the Zuul request lifecycle. There is no intermediate request created. Sorry for the hijack attempt. How it Works Zuul Request Lifecycle

@stiyyagura if it helps I was able to using a PRE filter to retrieve the x.509 certificate chain from the HttpRequest calculate the fingerprint and stick it into a custom header which was then used upstream.

stiyyagura commented 7 years ago

@scaryPonens Agree with you but in my case want to transform the request body from one format to another in filter and send it to the back end services.

ryanjbaxter commented 7 years ago

@stiyyagura I think the difference is that we are checking the RequestContext first

stiyyagura commented 7 years ago

thanks for pointers and clarifications.