Skip to content

Instantly share code, notes, and snippets.

@WeirdBob
Created November 21, 2017 12:55
Show Gist options
  • Save WeirdBob/b25569d461f0f54444d2c0eab51f3c48 to your computer and use it in GitHub Desktop.
Save WeirdBob/b25569d461f0f54444d2c0eab51f3c48 to your computer and use it in GitHub Desktop.
Spring cloud gateway response body modification
spring:
cloud:
gateway:
routes:
- id: test
uri: https://httpbin.org:443
predicates:
- Path=/uuid
- Method=GET
server:
port: 8080
package examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableAutoConfiguration
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>examples</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M5</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.0.0.M3</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>spring</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>
</project>
package examples;
import java.nio.charset.Charset;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Component
public class ToUppercaseGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return -2; // -1 is response write filter, must be called before that
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
// probably should reuse buffers
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
byte[] uppedContent = new String(content, Charset.forName("UTF-8")).toUpperCase().getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body); // if body is not a flux. never got there.
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build()); // replace response with decorator
}
}
@wujian0325
Copy link

when i use this to modify a large content,it usually read a part of my json string ,server has no response,do you have meet this question?how can i solve this

you can remove the Content-Length from headers to handle this problem;

@dalegaspi
Copy link

dalegaspi commented Jan 5, 2021

thanks for this gist. it steered me towards what i need to modify the response of a body via Spring Cloud Gateway. A lot can be learned from the source code of ModifyResponseBodyGatewayFilterFactory.java to maybe improve on this but for my purpose the gist works as it is.

edit: oof. that didn't last long 😝. the above approach will work to some degree but it is limited...like, for example, if you intend to modify a JSON response body prior to returning to the client, the above gist will not work (i know because i tried). also note that the gist doesn't take the decoders into account from upstream like here.

I found an article that tackled this and the solution seems to be a modification of ModifyResponseBodyGatewayFilterFactory.

edit 2: after tinkering around, i was able to write a quick and dirty custom filter that modifies response body based on what ModifyResponseBodyGatewayFilterFactorydoes today ...i wrote up a gist here.

@MdShohanurRahman
Copy link

https://gist.github.com/MdShohanurRahman/0d27148e7a1c9e05f6542f11c2ad98a0
here is the complete request and response log. You can follow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment