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

Allow modification of the response body #47

Closed
WeirdBob opened this issue Jun 21, 2017 · 10 comments
Closed

Allow modification of the response body #47

WeirdBob opened this issue Jun 21, 2017 · 10 comments
Milestone

Comments

@WeirdBob
Copy link

Hi,

I need to modify the body of both the request and the response (encryption/decryption).
As I see it, I can create a filter that sets a ServerHttpRequestDecorator in the exchange and override the getBody method, but I didn't find a way to do the same with the response. The WriteResponseFilter is the first filter and consequently the exchange used is the original exchange.
Would it be possible to add a ResponseBodyHandler interface to allow modifying the response body? Or did I overlook something?

Thanks in advance.

@WeirdBob
Copy link
Author

Nevermind, I just added a GlobalFilter with an order of -2 and it works.

@spencergibb spencergibb modified the milestone: 2.0.0.M1 Jul 6, 2017
@re6exp
Copy link
Contributor

re6exp commented Nov 17, 2017

I'm trying, but no result...
Would you please post any sample code how to change response body?

@WeirdBob
Copy link
Author

Hi,

I'll try to make a Gist to show you how I've done (I'll have to clean up corporate code first), but in short:

  • implement GlobalFilter & Ordered
  • important: the order has to be <-1 or else the standard NettyWriteResponseFilter will send the response before your filter gets a chance to be called
  • in the overriden filter method, create a ServerHttpResponseDecorator over exchange.getResponse(), override the writeWith method of this decorator and do your body modifications there (I had to cast the body from a Publisher<? extends DataBuffer> to a Flux to make it easier to modify), return super.writeWith(yourModifiedBodyFlux). Mutate the exchange and set your decorator as the new response.

Hope it makes sense. I have 2 filters that work that way (the first one removes some fields from the JSON response, the second one ciphers the body of the response in AES).
Not the cleanest code I've written though, maybe there is a better way.

@re6exp
Copy link
Contributor

re6exp commented Nov 21, 2017

Thanks a lot!
I'm new to reactive way, so I have to learn many things "in parallel mode" in short time...

And how do you cast the body from a Publisher<? extends DataBuffer> to a Flux?

I got it "works" somehow, but always with exception "only one subscriber allowed". And that way, as I understand, is wrong.

@re6exp
Copy link
Contributor

re6exp commented Nov 21, 2017

response.bufferFactory().allocateBuffer().asInputStream() reproduces nothing...

@WeirdBob
Copy link
Author

I made a small example here : https://gist.github.com/WeirdBob/b25569d461f0f54444d2c0eab51f3c48
it just converts the body into to uppercase.
you can launch it and try going to localhost:8080/uuid

@re6exp
Copy link
Contributor

re6exp commented Nov 21, 2017

Thank you very much!!!
I got hope!
You've made my day!!!

@re6exp
Copy link
Contributor

re6exp commented Nov 23, 2017

We can simplify response body modification by using a plain GatewayFilter:

  1. Create GatewayFilter implementation with the same filter() content as in the sample above;
  2. Create method to return an instance of the filter, e.g., bodyToUppercase() (if you prefer - just for simplification);
  3. Add filter to the router like:
.route("routeIdHere")
    .predicate(path("/deep/deep/hurray"))
    ...
    .filter(bodyToUppercase(), NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1)
    ...
.uri("lb://serverId")

So, it can be achieved with a plain GatewayFilter, by forcing the order pass. Using the GlobalFilter is not mandatory, but using GatewayFilter we have clear picture of the routes.

@MarkZhang1111
Copy link

How to append or modify the request body ? the length of the body may become longer, but the service behind the gateway received the request body have been truncated, some data lost.

@MarkZhang1111
Copy link

I solved this problem by modifying the length of the head:
To modify the header CONTENT_LENGTH,You must to create a new HttpHeaders, and then copy the orignal values and remove the CONTENT_LENGTH, add the correct length(If you do not do this , the request body will be truncated if you modify the request body and the body become longer )

    HttpHeaders myHeaders = new HttpHeaders();
    copyMultiValueMap(request.getHeaders(), myHeaders);
    myHeaders.remove(HttpHeaders.CONTENT_LENGTH);
    myHeaders.set(HttpHeaders.CONTENT_LENGTH, String.valueOf(len));
    
    Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
    newRequest = new ServerHttpRequestDecorator(newRequest) {
        @Override
        public Flux<DataBuffer> getBody() {	
            return bodyFlux;
        }
        
        @Override
        public HttpHeaders getHeaders() {
        	return myHeaders;           	
        }
    };

private static <K, V> void copyMultiValueMap(MultiValueMap<K,V> source, MultiValueMap<K,V> target) {
source.forEach((key, value) -> target.put(key, new LinkedList<>(value)));
}

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

No branches or pull requests

4 participants