Spring Cloud Gateway -- 修改RequestBody和Header

前言

目前项目有个需求,前端POST请求需要以xml形式传输,后端接口有些返回的是JSON格式有些返回的XML格式并且由于网络架构的需求,需要一个中间平台来处理这些请求。最终觉得利用Spring Cloud Gateway来实现。前端统POST请求统一用AJAX发送请求,中间平台将POST转换成XML形式,并且对相应的如果是xml形式的数据转成成JSON的格式进行返回。

读取和修改RequestBody

读取RequestBody的方法。
本人的一点猜测:这个方法可能是有缺陷的,因为在读取ResponseBody的时候由于后台的返回的数据量很大,好像是分了好几次发送,需要将其拼装在一起之后在做处理(后面的博客会讲到的)。故而猜测这里应该是同样的道理,不过由于请求参数不会很多,所有先不管后面有时间在进行测试。(后面确实发现有问题 —解决办法

    /**
     * 获取请求体中的字符串内容
     * @param serverHttpRequest
     * @return
     */
    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){
        //获取请求体
        Flux<DataBuffer> body = serverHttpRequest.getBody();
        StringBuilder sb = new StringBuilder();
        body.subscribe(buffer -> {
            byte[] bytes = new byte[buffer.readableByteCount()];
            buffer.read(bytes);
            DataBufferUtils.release(buffer);
            String bodyString = new String(bytes, StandardCharsets.UTF_8);
            sb.append(bodyString);
        });
        return sb.toString();
    }

对请求的方式进行判断,之后将数据转换成XML形式,并且需要重新传递。
重点

  1. 无论你修改了什么东西,都是需要new ServerHttpRequestDecorator 这个对象,并且调用你相对应的方法,例如我这边改了body就需要重写getBody这个方法。大家可以具体看看这个ServerHttpRequestDecorator的提供的方法就行了。
  2. 修改了Body 还需要修改CONTENT_LENGTH(下面会说的)
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest oldRequest= exchange.getRequest();
        URI uri = oldRequest.getURI();
        if("POST".equalsIgnoreCase(oldRequest.getMethodValue())) {
            Object requestBody = resolveBodyFromRequest(oldRequest);
            String xml = "";
            if(requestBody != null){
                xml = resolveBodyToXML(requestBody.toString());
            }
            //把request重新包装,继续传递
            ServerHttpRequest newRequest = null;
            try {
                byte[] bytes =  URLEncoder.encode(xml, "UTF-8").replaceAll("\\+", "%20").getBytes(StandardCharsets.UTF_8);
                DataBuffer bodyDataBuffer = stringBuffer(new String(bytes));
                Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
                newRequest = oldRequest.mutate().uri(uri).build();
                newRequest = new ServerHttpRequestDecorator(newRequest) {
                    @Override
                    public Flux<DataBuffer> getBody() {
                        return bodyFlux;
                    }
                };
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return chain.filter(exchange.mutate().request(newRequest).build());
        }else{
            return chain.filter(exchange);
        }

    }

修改Header

和修改Body同理。修改CONTENT_TYPE和CONTENT_LENGTH
我是把同一个代码拆开了,为了看的清楚一些。大家把他们拼起来就好了

         // 定义新的消息头
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(exchange.getRequest().getHeaders());
                // 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度
                int length = bytes.length;
                headers.remove(HttpHeaders.CONTENT_LENGTH);
                headers.setContentLength(length);
                headers.set(HttpHeaders.CONTENT_TYPE, "text/xml");
				newRequest = new ServerHttpRequestDecorator(newRequest) {
                    @Override
                    public HttpHeaders getHeaders() {
                        return headers;
                    }
                };

添加路由

这个就比较平常了

@EnableAutoConfiguration
@Configuration
public class ApiLocator {

    @Autowired
    private RequestFilter requestFilter;

    @Bean
    public RouteLocator myRoutes(RouteLocatorBuilder builder) {
        List list = new ArrayList();
        list.add(requestFilter);

        RouteLocatorBuilder.Builder routes = builder.routes();
        RouteLocatorBuilder.Builder serviceProvider = routes
                .route("route3",
                        r -> r.path(SERVICE)
                                .filters(f -> {
                            f.filters(list);
                            return f;
                        }).uri(URI));
        RouteLocator routeLocator = serviceProvider.build();
        return routeLocator;
    }
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐