import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import lombok.extern.slf4j.Slf4j;
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.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
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;
import reactor.util.annotation.NonNull;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
@Component
@Slf4j
public class GatewayGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
final long startTime = System.currentTimeMillis();
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
mutate.headers(x -> {
x.remove(Constant.XXXX_USER_INFO_HEADER_NAME);
x.remove(Constant.XXXX_FORWARD_SOURCE_HEADER_NAME);
});
mutate.header(Constant.XXXX_FORWARD_SOURCE_HEADER_NAME, ForwardSourceEnum.Gateway.getSource());
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
@NonNull
@Override
public Mono<Void> writeWith(@NonNull Publisher<? extends DataBuffer> body) {
String uri = exchange.getRequest().getURI().getPath();
if (body instanceof Flux) {
@SuppressWarnings("unchecked")
Flux<? extends DataBuffer> fluxDataBuffer = (Flux<? extends DataBuffer>) body;
//此处异常报错并不会打印日志,直接就fallback了
return super.writeWith(fluxDataBuffer.buffer()
.map(dataBuffer -> {
HttpStatus statusCode = this.getStatusCode();
DataBuffer responseBody = this.bufferFactory().join(dataBuffer);
UserInfo userInfo = Optional.ofNullable((UserInfo) exchange.getAttributes().get(Constant.XXXX_USER_INFO_HEADER_NAME))
.orElse(new UserInfo(false));
String ip = userInfo.getIp();
String uid = userInfo.getUid();
if (statusCode == null || statusCode.isError()) {
log.info("响应uri:{},耗时:{},uid:{},ip:{},内部服务异常响应:{}", uri,
System.currentTimeMillis() - startTime, uid, ip, StandardCharsets.UTF_8.decode(responseBody.asByteBuffer()));
ReturnModelEnum returnModelEnum = (statusCode != null && statusCode.is4xxClientError())
? ReturnModelEnum.NOT_FOUND_PATH : ReturnModelEnum.ERROR;
responseBody = this.bufferFactory().wrap(JSON.toJSONBytes(ReturnModel.fail(returnModelEnum), JSONWriter.Feature.WriteMapNullValue));
this.setStatusCode(HttpStatus.OK);
this.getHeaders().setContentType(MediaType.APPLICATION_JSON);
}
MediaType contentType = this.getHeaders().getContentType();
long l = System.currentTimeMillis() - startTime;
if (contentType == null) {
log.info("响应uri:{},耗时:{},uid:{},ip:{},响应不打印响应体,contentType:null", uri, l, uid, ip);
return responseBody;
}
String contentTypeString = contentType.toString();
if (contentTypeString.startsWith(MediaType.APPLICATION_JSON_VALUE)) {
log.info("响应uri:{},耗时:{},uid:{},ip:{},响应体:{}", uri, l, uid, ip, StandardCharsets.UTF_8.decode(responseBody.asByteBuffer()));
} else {
log.info("响应uri:{},耗时:{},uid:{},ip:{},响应体类型:{},不打印响应体", uri, l, uid, ip, contentTypeString);
}
return responseBody;
}
)
);
}
Flux<? extends DataBuffer> fluxBody = Flux.from(body);
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer dataBuffer = dataBufferFactory.join(dataBuffers);
log.info("响应uri:{},耗时:{},响应体:{}", uri, System.currentTimeMillis() - startTime, StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()));
return this.bufferFactory().wrap(dataBuffer.asByteBuffer());
}));
}
};
ServerWebExchange newExchange = exchange.mutate().request(request).response(responseDecorator).build();
return chain.filter(newExchange)
.then(Mono.fromRunnable(() -> {
newExchange.getAttributes().clear();
}));
}
@Override
public int getOrder() {
return -1;
}
}
Spring Cloud Gateway 3 全局过滤器中修改响应体案例 2
于 2023-09-26 19:32:20 首次发布