如何使用 Spring Cloud Gateway 在 GatewayFilter 中设置请求正文

How to set request body in GatewayFilter with Spring Cloud Gateway

我有一个GatewayFilter.It收到一个请求,

  1. 先检查签名(代码省略)
  2. 查询一些需要的参数
  3. 将它们添加到请求中
  4. 终于转发了请求

如何将查询参数添加到请求正文中作为 json?

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)  {
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();



        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = getBodyInsert(exchange);
        CachedBodyOutputMessage outputMessage = getCachedBodyOutputMessage(exchange);
        
        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
           //1.checks the signature  (omit)
    
            //2. get these parameters remotely
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("calcPackageId", 10000);
            paramMap.put("userId", 10001);
            paramMap.put("barId", 10002);
            String body = JSON.toJSON(paramMap);
            //3. todo how to set request body {"calcPackageId":"10000","barId":"10002","userId":"10001"} ??????

          //4 forwards the request
            return chain.filter(exchange.mutate().request(newRequest).build());
        }));
    }



   //not important

    private BodyInserter<Mono<String>, ReactiveHttpOutputMessage> getBodyInsert(ServerWebExchange exchange) {
        ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);
        Mono<String> rawBody = serverRequest.bodyToMono(String.class).map(s -> s);
        return BodyInserters.fromPublisher(rawBody, String.class);

    }

    private CachedBodyOutputMessage getCachedBodyOutputMessage(ServerWebExchange exchange) {
        HttpHeaders tempHeaders = new HttpHeaders();
        tempHeaders.putAll(exchange.getRequest().getHeaders());
        tempHeaders.remove(HttpHeaders.CONTENT_LENGTH);
        return new CachedBodyOutputMessage(exchange, tempHeaders);
    }

一种方法是将 ModifyRequestBodyGatewayFilterFactory bean 注入过滤器。它由 Spring Cloud Gateway 的 Autoconfig 自动初始化。

然后,您可以向它的 .apply 方法传递一个 RewriteFunction 对象。有了这个,你可以创建一个自定义的 class 来实现 RewriteFunction 接口,用你需要的数据初始化它,然后 return 它作为新的主体。例如:

public class SomeFilter implements GatewayFilter {

    private final ModifyRequestBodyGatewayFilterFactory factory;

    public SomeFilter(ModifyRequestBodyGatewayFilterFactory factory) {
        this.factory = factory;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Map<String, Integer> someMap = new HashMap<>();
        someMap.put("AbC", 100);

        ModifyRequestBodyGatewayFilterFactory.Config cfg = new ModifyRequestBodyGatewayFilterFactory.Config();
        cfg.setRewriteFunction(String.class, String.class, new SomeRewriteFunction(someMap));

        GatewayFilter modifyBodyFilter = factory.apply(cfg);

        return modifyBodyFilter.filter(exchange, ch -> Mono.empty())
                .then(chain.filter(exchange));
    }
}

和重写函数:

public class SomeRewriteFunction implements RewriteFunction<String, String> {

    private final Map<String, Integer> values;

    public SomeRewriteFunction(Map<String, Integer> values) {
        this.values = values;
    }

    @Override
    public Publisher<String> apply(ServerWebExchange serverWebExchange, String oldBody) {
        /* do things here */
        /* example: */
        try {
            String newBody = new ObjectMapper().writeValueAsString(values);
            return Mono.just(newBody);
        } catch (Exception e) {
            /* error parsing values to json, do something else */
            return Mono.just(oldBody);
        }
    }
}

另一种方法是注入一个装饰器,然后修改它的getBody()方法:

public class SomeFilter implements GatewayFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Map<String, Integer> someMap = new HashMap<>();
        someMap.put("AbC", 100);

        SomeDecorator requestDecorator = new SomeDecorator(exchange.getRequest(), someMap);

        return chain.filter(exchange.mutate().request(requestDecorator).build());
    }
}

和装饰者:

public class SomeDecorator extends ServerHttpRequestDecorator {

    private final Map<String, Integer> values;

    public SomeDecorator(ServerHttpRequest delegate, Map<String, Integer> values) {
        super(delegate);
        this.values = values;
    }

    @Override
    public Flux<DataBuffer> getBody() {

        try {
            String newBody = new ObjectMapper().writeValueAsString(values);
            DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
            DefaultDataBuffer buffer = factory.wrap(newBody.getBytes());
            return Flux.just(buffer);
        } catch (JsonProcessingException e) {
            return super.getBody();
        }
    }
}