|
@@ -4,30 +4,21 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import org.reactivestreams.Publisher;
|
|
import org.reactivestreams.Publisher;
|
|
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
|
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
|
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
|
|
-import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
|
|
|
|
|
-import org.springframework.cloud.gateway.support.BodyInserterContext;
|
|
|
|
|
import org.springframework.core.Ordered;
|
|
import org.springframework.core.Ordered;
|
|
|
import org.springframework.core.io.buffer.DataBuffer;
|
|
import org.springframework.core.io.buffer.DataBuffer;
|
|
|
import org.springframework.core.io.buffer.DataBufferFactory;
|
|
import org.springframework.core.io.buffer.DataBufferFactory;
|
|
|
import org.springframework.core.io.buffer.DataBufferUtils;
|
|
import org.springframework.core.io.buffer.DataBufferUtils;
|
|
|
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
|
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
|
|
-import org.springframework.http.HttpHeaders;
|
|
|
|
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
|
-import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
|
|
|
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
|
|
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
|
|
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
|
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.stereotype.Component;
|
|
|
-import org.springframework.web.reactive.function.BodyInserter;
|
|
|
|
|
-import org.springframework.web.reactive.function.BodyInserters;
|
|
|
|
|
-import org.springframework.web.reactive.function.server.HandlerStrategies;
|
|
|
|
|
-import org.springframework.web.reactive.function.server.ServerRequest;
|
|
|
|
|
import org.springframework.web.server.ServerWebExchange;
|
|
import org.springframework.web.server.ServerWebExchange;
|
|
|
import reactor.core.publisher.Flux;
|
|
import reactor.core.publisher.Flux;
|
|
|
import reactor.core.publisher.Mono;
|
|
import reactor.core.publisher.Mono;
|
|
|
|
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
-import java.util.function.Consumer;
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* HTTP 请求响应日志过滤器
|
|
* HTTP 请求响应日志过滤器
|
|
@@ -60,80 +51,22 @@ public class AccessLogFilter implements GlobalFilter, Ordered {
|
|
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
|
|
String requestId = UUID.randomUUID().toString().replace("-", "");
|
|
String requestId = UUID.randomUUID().toString().replace("-", "");
|
|
|
long requestTime = System.currentTimeMillis();
|
|
long requestTime = System.currentTimeMillis();
|
|
|
- Consumer<HttpHeaders> headers = header -> {
|
|
|
|
|
- header.set("x-request-id", requestId);
|
|
|
|
|
- header.set("x-request-time", ""+requestTime);
|
|
|
|
|
- };
|
|
|
|
|
- exchange.getRequest().mutate().headers(headers).build();
|
|
|
|
|
|
|
|
|
|
- // 获取响应体
|
|
|
|
|
- ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange);
|
|
|
|
|
- return chain.filter(exchange.mutate().response(decoratedResponse).build());
|
|
|
|
|
- //return getRequestBody(exchange, chain);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 1. 构建包含新 Header 的请求
|
|
|
|
|
+ ServerHttpRequest newRequest = exchange.getRequest().mutate()
|
|
|
|
|
+ .header("x-request-id", requestId)
|
|
|
|
|
+ .header("x-request-time", String.valueOf(requestTime))
|
|
|
|
|
+ .build();
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 获取 request body
|
|
|
|
|
- *
|
|
|
|
|
- * @param
|
|
|
|
|
- * @return
|
|
|
|
|
- * @date 2025-07-18 14:44:53
|
|
|
|
|
- */
|
|
|
|
|
- private Mono<Void> getRequestBody(ServerWebExchange exchange, GatewayFilterChain chain) {
|
|
|
|
|
- ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
|
|
|
|
|
- Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
|
|
|
|
|
- .flatMap(body ->{
|
|
|
|
|
- //gatewayLog.setRequestBody(body);
|
|
|
|
|
- return Mono.just(body);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ // 2. 先生成包含新请求的 Exchange
|
|
|
|
|
+ ServerWebExchange newExchange = exchange.mutate()
|
|
|
|
|
+ .request(newRequest)
|
|
|
|
|
+ .build();
|
|
|
|
|
|
|
|
- HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
- headers.putAll(exchange.getRequest().getHeaders());
|
|
|
|
|
- // the new content type will be computed by bodyInserter
|
|
|
|
|
- // and then set in the request decorator
|
|
|
|
|
- headers.remove(HttpHeaders.CONTENT_LENGTH);
|
|
|
|
|
- CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
|
|
|
|
|
|
|
+ // 3. 将 newExchange 传给装饰器(关键:传入的是带 Header 的对象)
|
|
|
|
|
+ ServerHttpResponseDecorator decoratedResponse = recordResponseLog(newExchange);
|
|
|
|
|
|
|
|
- // 通过 BodyInserter 插入 body(支持修改body), 避免 request body 只能获取一次
|
|
|
|
|
- BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
|
|
|
|
|
- return bodyInserter.insert(outputMessage,new BodyInserterContext())
|
|
|
|
|
- .then(Mono.defer(() -> {
|
|
|
|
|
- // 重新封装请求
|
|
|
|
|
- ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);
|
|
|
|
|
-
|
|
|
|
|
- // 记录响应日志
|
|
|
|
|
- ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange);
|
|
|
|
|
- return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build())
|
|
|
|
|
- .then(Mono.fromRunnable(() -> {
|
|
|
|
|
- // 打印日志
|
|
|
|
|
- //writeAccessLog(gatewayLog);
|
|
|
|
|
- }));
|
|
|
|
|
- }));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers,
|
|
|
|
|
- CachedBodyOutputMessage outputMessage) {
|
|
|
|
|
- return new ServerHttpRequestDecorator(exchange.getRequest()) {
|
|
|
|
|
- @Override
|
|
|
|
|
- public HttpHeaders getHeaders() {
|
|
|
|
|
- long contentLength = headers.getContentLength();
|
|
|
|
|
- HttpHeaders httpHeaders = new HttpHeaders();
|
|
|
|
|
- httpHeaders.putAll(super.getHeaders());
|
|
|
|
|
- if (contentLength > 0) {
|
|
|
|
|
- httpHeaders.setContentLength(contentLength);
|
|
|
|
|
- } else {
|
|
|
|
|
- // TODO: this causes a 'HTTP/1.1 411 Length Required' // on
|
|
|
|
|
- // httpbin.org
|
|
|
|
|
- httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
|
|
|
|
|
- }
|
|
|
|
|
- return httpHeaders;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- @Override
|
|
|
|
|
- public Flux<DataBuffer> getBody() {
|
|
|
|
|
- return outputMessage.getBody();
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ return chain.filter(newExchange.mutate().response(decoratedResponse).build());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|