SpringCloud-gateway全局GlobalFilter获取post的请求参数

gateway里面可以自定义普通filter,也可以创建自定义的GlobalFilter。

GlobalFilter实际上还是比较常用的,譬如可以在GlobalFilter里做日志处理、认证鉴权等,这里就涉及一个获取到请求参数的问题。

用户发起Get、Post请求,经过网关gateway,gateway的GlobalFilter进行拦截——获取参数——处理并包装——转到下一级,我们就来看看该怎么获取请求的参数。

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

Get请求

/**
 * @author wuweifeng wrote on 2018/10/24.
 */
@Component
public class AuthSignFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("authToken");
        if (token == null || token.isEmpty()) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);

    }

    @Override
    public int getOrder() {
        return 0;
    }
}

这里定义了一个GlobalFilter,如果是Get请求,就可以通过getQueryParams().getFirst("你的key")的方式来获取。

可以看到已经获取到了Get请求的参数,如果有多个请求参数,就可以通过遍历exchange.getRequest().getQueryParams()来获取。

Post请求

post请求的传参获取相对比较麻烦一些,gateway采用了webflux的方式来封装的请求体。

我们知道post常用的两种传参content-type是application/x-www-form-urlencoded和application/json,这两种方式还是有区别的。

下面来看一下获取Post请求体的方法,关于webflux获取内容的教程请看上一篇

package com.example.gateway.filter;

import io.netty.buffer.ByteBufAllocator;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author wuweifeng wrote on 2018/10/24.
 */
@Component
public class AuthSignFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String method = serverHttpRequest.getMethodValue();
        if ("POST".equals(method)) {
            //从请求里获取Post请求体
            String bodyStr = resolveBodyFromRequest(serverHttpRequest);
            //TODO 得到Post请求的请求参数后,做你想做的事

            //下面的将请求体再次封装写回到request里,传到下一级,否则,由于请求体已被消费,后续的服务将取不到值
            URI uri = serverHttpRequest.getURI();
            ServerHttpRequest request = serverHttpRequest.mutate().uri(uri).build();
            DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
            Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);

            request = new ServerHttpRequestDecorator(request) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return bodyFlux;
                }
            };
            //封装request,传给下一级
            return chain.filter(exchange.mutate().request(request).build());
        } else if ("GET".equals(method)) {
            Map requestQueryParams = serverHttpRequest.getQueryParams();
            //TODO 得到Get请求的请求参数后,做你想做的事

            return chain.filter(exchange);
        }
        return chain.filter(exchange);
    }

    /**
     * 从Flux<DataBuffer>中获取字符串的方法
     * @return 请求体
     */
    private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
        //获取请求体
        Flux<DataBuffer> body = serverHttpRequest.getBody();

        AtomicReference<String> bodyRef = new AtomicReference<>();
        body.subscribe(buffer -> {
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            DataBufferUtils.release(buffer);
            bodyRef.set(charBuffer.toString());
        });
        //获取request body
        return bodyRef.get();
    }

    private DataBuffer stringBuffer(String value) {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);

        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

这里有详细的获取Post请求体的方法,无论是application/x-www-form-urlencoded和application/json都可以通过上面的方式。

看结果:

下面是@RequestBody形式的

这是后端消费的服务代码

也能正常接收@RequestBody请求并解析。

 

 

 

### 如何在 Spring Cloud Gateway获取 HTTP 请求对象及参数 #### 使用 `ServerWebExchange` 获取请求信息 在 Spring Cloud Gateway 的环境中,所有的请求处理都是基于 `ServerWebExchange` 对象完成的。此对象提供了访问当前HTTP请求和响应的能力[^1]。 ```java @Component public class CustomGlobalFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 打印请求路径 System.out.println("Request Path: " + request.getURI().getPath()); // 处理逻辑... return chain.filter(exchange); } } ``` #### 解决 POST 请求体只能读取一次的问题 由于 Reactor Netty 和 WebFlux 的特性,在 Spring Cloud Gateway 中直接读取请求体会消耗掉输入流,使得后续处理器无法再读取该数据。为了克服这个问题,可以采用缓存请求体的方式: - 将原始请求转换成新的带有相同属性但可重复读取的内容的新请求- 利用自定义过滤器来实现上述功能[^2]。 ```java import org.springframework.core.io.buffer.DataBuffer; import reactor.core.publisher.Flux; // 缓存请求体并创建新请求的方法 private static ServerHttpRequest copyRequestBody(ServerHttpRequest request) throws IOException { Flux<DataBuffer> cachedBody = request.getBody() .cache(); ServerHttpRequestDecorator decoratedRequest = new ServerHttpRequestDecorator(request) { @Override public Flux<DataBuffer> getBody() { return cachedBody; } }; return decoratedRequest; } ``` #### 日志记录中的应用实例 当希望在网关层面对所有进入系统的 API 调用做统一的日志管理时,可以通过全局过滤器截获每一个到来的请求,并从中提取必要的元数据以及可能存在的 JSON 形式的负载内容进行记录。 ```java @Slf4j @Component public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> { private final ObjectMapper objectMapper = new ObjectMapper(); public LoggingFilter() { super(Config.class); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { try { String bodyString = extractBodyAsString(exchange); log.info("Received Request:\nMethod:{}\nPath:{}\nHeaders:{}\nBody:{}", exchange.getRequest().getMethod(), exchange.getRequest().getURI().getPath(), exchange.getRequest().getHeaders(), bodyString); return chain.filter(exchange.mutate().request(copyRequestBody(exchange.getRequest())).build()); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } }; } private String extractBodyAsString(ServerWebExchange exchange) throws Exception { StringBuilder builder = new StringBuilder(); exchange.getAttributeOrDefault("cached_request_body", "") .toString() .chars() .forEach(c -> builder.append((char)c)); if(builder.length() == 0){ return ""; } return builder.toString(); } public static class Config {} } ```
评论 66
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天涯泪小武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值