最近項目中使用spring cloud gateway作為網(wǎng)關(guān)狸眼,因為歷史原因沉桌,有定制化的需求谢鹊,要進(jìn)行二次開發(fā),碰到一些問題留凭,在這先記錄一下佃扼,以后有時間再詳細(xì)補(bǔ)充。
這次是需要在請求的入口和出口分別打印報文信息蔼夜。處理GET請求的時候還好兼耀,POST請求有時候參數(shù)放在requestBody中,而且2.X的版本之后,spring cloud使用 spring5 webflux方式編程瘤运,在filter中處理過一次的requestBody窍霞,下游訂閱者無法接收,網(wǎng)上找了很多都是對某個具體路由在編碼中進(jìn)行配置拯坟,如下
.route("rewrite_request_upper", r -> r.host("*.rewriterequestupper.org")
.filters(f -> f.prefixPath("/httpbin")
.addResponseHeader("X-TestHeader", "rewrite_request_upper")
.modifyRequestBody(String.class, String.class,
(exchange, s) -> {
return Mono.just(s.toUpperCase()+s.toUpperCase());
})
).uri(uri)
)
而我的項目中使用了動態(tài)路由但金,未來路由的增減是很常見的事,這種方式?jīng)]法對所有的路由進(jìn)行請求攔截郁季,很不靈活冷溃。
最后參看源碼,發(fā)現(xiàn)了一個ModifyRequestBodyGatewayFilterFactory類巩踏,里邊有對requestBody的處理邏輯秃诵,然后把源碼照搬了過來,由于我只需要打印日志塞琼,稍微改一下就行菠净,別的代碼就保留了。
/**
* 參照 ModifyRequestBodyGatewayFilterFactory 寫的一個處理 requestBody的filter
*
* @author huangting
*/
@Component
public class RequestHandlerFilter implements GlobalFilter, Ordered {
private static final Logger logger = LoggerFactory.getLogger(RequestHandlerFilter.class);
private static final String METHOD_POST = "POST";
private static final String METHOD_GET = "GET";
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//POST和get的處理邏輯不一樣
if (METHOD_POST.equals(exchange.getRequest().getMethodValue())) {
ServerRequest serverRequest = new DefaultServerRequest(exchange);
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(requestBody -> {
//打印請求報文
logRequestLog(request, requestBody);
return Mono.just(requestBody);
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
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);
return bodyInserter.insert(outputMessage, new BodyInserterContext())
// .log("modify_request", Level.INFO)
.then(Mono.defer(() -> {
ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
return chain.filter(exchange.mutate().request(decorator).build());
}));
} else {
//打印請求報文
logRequestLog(request, null);
chain.filter(exchange);
}
return chain.filter(exchange);
}
ServerHttpRequestDecorator decorate(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();
}
};
}
/**
* 打印請求日志
*
* @param request
*/
public void logRequestLog(ServerHttpRequest request, String requestBody) {
String requestParam = request.getQueryParams().toString();
logger.info("請求報文 URL:{},Method:{},headers:{},param:{},requestbody:{}", request.getURI().getPath(), request.getMethod(), request.getHeaders(), requestParam, requestBody);
}
@Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -3;
}
}
需要注意的是 彪杉,我的spring cloud 是 2.1.3毅往,Greenwich.SR3的版本,CachedBodyOutputMessage 和 DefaultServerRequest這里兩個類的權(quán)限變成了spring私有的了派近,需要把他們copy出來作為自己項目中的類攀唯,以上的代碼有用到