## spring cloud gateway 复用 request body

spring cloud gateway 复用 request body(二)

实现方案:

  • 每个路由单独配置
  • 全局配置

单独配置

使用 spring cloud gateway 默认提供 的 ReadBodyRoutePredicateFactory

// ReadBodyRoutePredicateFactory.java
public AsyncPredicate<ServerWebExchange> applyAsync(Config config) {
    return new AsyncPredicate<ServerWebExchange>() {
        @Override
        public Publisher<Boolean> apply(ServerWebExchange exchange) {
            Class inClass = config.getInClass();

            Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
            Mono<?> modifiedBody;
            // We can only read the body from the request once, once that happens if
            // we try to read the body again an exception will be thrown. The below
            // if/else caches the body object as a request attribute in the
            // ServerWebExchange so if this filter is run more than once (due to more
            // than one route using it) we do not try to read the request body
            // multiple times
            if (cachedBody != null) {
                try {
                    boolean test = config.predicate.test(cachedBody);
                    exchange.getAttributes().put(TEST_ATTRIBUTE, test);
                    return Mono.just(test);
                }
                catch (ClassCastException e) {
                    if (log.isDebugEnabled()) {
                        log.debug("Predicate test failed because class in predicate "
                                  + "does not match the cached body object", e);
                    }
                }
                return Mono.just(false);
            }
            else {
                return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
                                                                         (serverHttpRequest) -> ServerRequest
                                                                         .create(exchange.mutate().request(serverHttpRequest).build(), messageReaders)
                                                                         .bodyToMono(inClass).doOnNext(objectValue -> exchange.getAttributes()
                                                                                                       .put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
                                                                         .map(objectValue -> config.getPredicate().test(objectValue)));
            }
        }

        @Override
        public Object getConfig() {
            return config;
        }

        @Override
        public String toString() {
            return String.format("ReadBody: %s", config.getInClass());
        }
    };
}

原理就是,将 body数据取出转成 string, 然后放在 exchange 的 CACHE_REQUEST_BODY_OBJECT_KEY 属性中,要使用时,取出这个属性值 就行

配置:

1.application.yml配置:

spring:
  application:
    name: nacos-gateway
  cloud:
    gateway:
      routes:
        - id: nacos-user_3
          uri: http://localhost:5558
          predicates:
            - Path=/r/**
            - name: ReadBody #使用ReadBodyPredicateFactory断言,将body读入缓存
              args:
                inClass: 'java.lang.String'
                predicate: '#{@bodyPredicate}' #注入实现predicate接口类
          filters:
            - StripPrefix=1

断言:

// el 表达式取
@Bean
public Predicate bodyPredicate() {
    return o -> true;
}

过滤器:

@Component
@Slf4j
public class ReadBodyFilter implements GlobalFilter {

    private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            log.info("POST form-data request ignore, query params: {}", exchange.getRequest().getQueryParams());
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        HttpMethod method = request.getMethod();
        if (method.matches("GET")) {
            log.info("GET request param: {}", request.getQueryParams());
        } else if (method.matches("POST")) {
            Object requestBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
            log.info("POST request param: {}-body: {}", request.getQueryParams(), requestBody);
        }
        return chain.filter(exchange);
    }
}

全局过滤器

ReadBodyRoutePredicateFactory 改造一下:

@Slf4j
public class GlobalCacheBodyStringRequestFilter implements GlobalFilter, Ordered {

    private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";

    private List<HttpMessageReader<?>> messageReaders;

    public GlobalCacheBodyStringRequestFilter(List<HttpMessageReader<?>> readers) {
        this.messageReaders = readers;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            return chain.filter(exchange);
        }
        log.info("cache body...");
        return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
                (serverHttpRequest) -> ServerRequest.create(exchange.mutate().request(serverHttpRequest).build(), messageReaders)
                        .bodyToMono(String.class).doOnNext(objectValue -> exchange.getAttributes()
                                .put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue)
                        ).then(Mono.defer(() -> chain.filter(exchange)))
        );
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 1;
    }
}


@Bean
public GlobalCacheBodyStringRequestFilter globalCacheBodyStringRequestFilter(ServerCodecConfigurer configurer) {
    return new GlobalCacheBodyStringRequestFilter(configurer.getReaders());
}

模拟重复取数据:

GlobalTraceFilter 日志拦截:

@Component
@Slf4j
public class GlobalTraceFilter implements GlobalFilter, Ordered {

    private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            log.info("POST form-data request ignore, query params: {}", exchange.getRequest().getQueryParams());
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        HttpMethod method = request.getMethod();
        if (method.matches("GET")) {
            log.info("GET request param: {}", request.getQueryParams());
        } else if (method.matches("POST")) {
            Object bodyStr = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
            log.info("POST request param str: {}-body: {}", request.getQueryParams(), bodyStr);
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 2;
    }
}

GlobalAuthRequestFilter 认证拦截

@Component
@Slf4j
public class GlobalAuthRequestFilter implements GlobalFilter {

    private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject";

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        String contentType = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        if (StringUtils.hasText(contentType) && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        HttpMethod method = request.getMethod();
        if (method.matches("GET")) {
            log.info("Auth GET request param: {}", request.getQueryParams());
        } else if (method.matches("POST")) {
            Object bodyStr = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY);
            log.info("POST request param str: {}-body: {}", request.getQueryParams(), bodyStr);
        }
        return chain.filter(exchange);
    }
}

结果:

c.e.nacosgatew.config.GlobalTraceFilter  : POST request param str: {a1=[aa], b1=[cc]}-body: { "a": "hello"}
c.e.n.config.GlobalAuthRequestFilter     : POST request param str: {a1=[aa], b1=[cc]}-body: { "a": "hello"}

good luck!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值