-
-
Save WeirdBob/b25569d461f0f54444d2c0eab51f3c48 to your computer and use it in GitHub Desktop.
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 | |
} | |
} |
leiyazhen
commented
Dec 13, 2018
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
have you solved it? i meet same question
i have the same problem,have you solved it?
me too
Seems so complicated when all you want is to just pass a simple response when an Authorization
header is missing
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
have you solved it? I have the same question.
There are one problem with your ToUppercaseGlobalFilter:
- When you are done with the buffer, you should release it with the following code. Or under stress test, netty will detect memory leak
DataBufferUtils.release(buffer);
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 should set the response length to a new value with the following code, or the response body may be truncated
response.getHeaders().setContentLength(YOUR_NEW_RESPONSE_LENGTH_IN_BYTES);
Seems so complicated when all you want is to just pass a simple response when an
Authorization
header is missing
true
Can you please elaborate how can you do the same in requestBody. I have a requirement to log it for audit purposes.
Can you please elaborate how can you do the same in requestBody. I have a requirement to log it for audit purposes.
Sure, man. Refer to my demo code here
@chenkunyun My requirements are to log all incoming request with request body and also make another call to audit service passing the request body and api. The example you have provided is for response body. Can you please tell how would you do that for incoming request ?
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest originalRequest = exchange.getResponse();
// This doesn't work
String requestBody = originalRequest.getRequestBody().toString();
ServerHttpRequestDecorator decoratedResponse = new ServerHttpRequestDecorator(originalRequest) {
// what needs to be implemented here ?
}
};
log.info("Incoming request is [{}] for path [{}]" requestBody, originalRequest.getPath() );
return chain.filter(exchange.mutate().response(originalRequest).build()); // replace response with decorator
}
I am using DiscoveryClient Route Definition Locator to build routes so cannot add ReadBodyPredicateFactory as it is JAVA DSL based config.
@arpitkh12 have you found solution yet?
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;
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 ModifyResponseBodyGatewayFilterFactory
does today ...i wrote up a gist here.
https://gist.github.com/MdShohanurRahman/0d27148e7a1c9e05f6542f11c2ad98a0
here is the complete request and response log. You can follow.