spring cloud gateway 二次开发之 处理 requestBody

最近项目中使用spring cloud gateway作为网关,因为历史原因,有定制化的需求,要进行二次开发,碰到一些问题,在这先记录一下,以后有时间再详细补充。

这次是需要在请求的入口和出口分别打印报文信息。处理GET请求的时候还好,POST请求有时候参数放在requestBody中,而且2.X的版本之后,spring cloud使用 spring5 webflux方式编程,在filter中处理过一次的requestBody,下游订阅者无法接收,网上找了很多都是对某个具体路由在编码中进行配置,如下


.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)
                )

而我的项目中使用了动态路由,未来路由的增减是很常见的事,这种方式没法对所有的路由进行请求拦截,很不灵活。

最后参看源码,发现了一个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 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 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这里两个类的权限变成了spring私有的了,需要把他们copy出来作为自己项目中的类,以上的代码有用到

你可能感兴趣的:(spring cloud gateway 二次开发之 处理 requestBody)