Spring Cloud Gateway 核心工具类

以下为每个核心工具类提供可直接运行的代码示例,覆盖上下文操作、路由处理、请求响应修改、过滤器开发、异常处理等场景,所有示例基于 Spring Cloud Gateway 3.1.x(主流稳定版本)。

一、上下文操作工具类

1. ServerWebExchangeUtils(核心)

场景:获取路由信息、修改转发 URL、标记原始请求 URL

java

运行

复制代码
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.cloud.gateway.route.Route;
import java.net.URI;

// 网关过滤器中使用
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 1. 获取匹配的路由(简化写法)
    Route route = ServerWebExchangeUtils.getRoute(exchange);
    if (route == null) {
        return chain.filter(exchange); // 无匹配路由,直接放行
    }
    System.out.println("匹配的路由ID:" + route.getId());
    System.out.println("路由目标URI:" + route.getUri());

    // 2. 记录原始请求URL(重写URL必备,避免丢失原地址)
    ServerWebExchangeUtils.addOriginalRequestUrl(exchange, exchange.getRequest().getURI());

    // 3. 修改网关转发的目标URL(同机房收敛场景:转发到上海机房实例)
    URI targetUri = URI.create("http://10.0.0.100:8080/order");
    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, targetUri);

    // 4. 判断是否匹配到路由
    boolean isMatched = ServerWebExchangeUtils.isRouteMatched(exchange);
    System.out.println("路由匹配状态:" + isMatched);

    return chain.filter(exchange);
}

2. AttributeAccessorSupport(扩展示例)

场景:自定义上下文属性存取(过滤器间传递数据)

java

运行

复制代码
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.ServerWebExchangeDecorator;
import org.springframework.core.AttributeAccessorSupport;

// 自定义Exchange(继承扩展AttributeAccessorSupport)
public class CustomServerWebExchange extends ServerWebExchangeDecorator {
    private final AttributeAccessorSupport attributes = new AttributeAccessorSupport();

    public CustomServerWebExchange(ServerWebExchange delegate) {
        super(delegate);
    }

    // 重写属性存取方法
    @Override
    public <T> T getAttribute(String name) {
        T attr = super.getAttribute(name);
        return attr != null ? attr : (T) attributes.getAttribute(name);
    }

    @Override
    public void setAttribute(String name, Object value) {
        super.setAttribute(name, value);
        attributes.setAttribute(name, value);
    }

    // 自定义属性:跨过滤器传递灰度标记
    public void setGrayFlag(boolean isGray) {
        setAttribute("gray_flag", isGray);
    }

    public Boolean getGrayFlag() {
        return getAttribute("gray_flag");
    }
}

// 使用示例
CustomServerWebExchange customExchange = new CustomServerWebExchange(exchange);
customExchange.setGrayFlag(true);
System.out.println("灰度标记:" + customExchange.getGrayFlag());

二、路由匹配 / 构建工具类

1. RoutePredicateFactory(自定义断言)

场景:自定义机房匹配断言(仅允许上海机房请求通过)

java

运行

复制代码
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

@Component
public class IdcRoutePredicateFactory extends AbstractRoutePredicateFactory<IdcRoutePredicateFactory.Config> {
    // 配置参数名
    public static final String IDC_KEY = "idc";

    public IdcRoutePredicateFactory() {
        super(Config.class);
    }

    // 配置参数列表(供yml配置使用)
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList(IDC_KEY);
    }

    // 断言逻辑:请求头X-Idc匹配配置值则通过
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String requestIdc = exchange.getRequest().getHeaders().getFirst("X-Idc");
            return config.getIdc().equals(requestIdc);
        };
    }

    // 配置类(接收yml中的参数)
    public static class Config {
        private String idc;

        public String getIdc() {
            return idc;
        }

        public void setIdc(String idc) {
            this.idc = idc;
        }
    }
}

// yml配置使用该断言
/*
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
            - Idc=shanghai # 自定义机房断言
*/

exchange->等同于下面实现代码,lambda特性

等价于手动实现Predicate接口

复制代码
return new Predicate<ServerWebExchange>() {
    // 这里的test方法是Predicate接口要求必须实现的!
    @Override
    public boolean test(ServerWebExchange exchange) { // 定义exchange参数
        String requestIdc = exchange.getRequest().getHeaders().getFirst("X-Idc");
        return config.getIdc().equals(requestIdc);
    }
};

2. RouteLocatorBuilder(编程式构建路由)

场景:动态构建多机房路由规则

java

运行

复制代码
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DynamicRouteConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // 上海机房订单服务路由
                .route("order-shanghai", r -> r
                        .header("X-Idc", "shanghai") // 机房头匹配
                        .and().path("/order/**")     // 路径匹配
                        .uri("lb://order-service-shanghai")) // 上海机房服务
                // 北京机房订单服务路由
                .route("order-beijing", r -> r
                        .header("X-Idc", "beijing")
                        .and().path("/order/**")
                        .uri("lb://order-service-beijing"))
                // 默认路由(无机房头)
                .route("order-default", r -> r
                        .path("/order/**")
                        .uri("lb://order-service-default"))
                .build();
    }
}

3. RouteDefinitionWriter(动态添加路由)

场景:运行时新增路由(无需重启网关)

java

运行

复制代码
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.builder.PredicateDefinitionBuilder;
import org.springframework.cloud.gateway.route.builder.UriDefinitionBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class DynamicRouteController {

    private final RouteDefinitionWriter routeDefinitionWriter;

    public DynamicRouteController(RouteDefinitionWriter routeDefinitionWriter) {
        this.routeDefinitionWriter = routeDefinitionWriter;
    }

    // 接口:POST /add-route
    @PostMapping("/add-route")
    public ResponseEntity<String> addRoute(@RequestBody RouteDefinition route) {
        // 示例:手动构建路由(也可接收JSON参数)
        RouteDefinition newRoute = new RouteDefinition();
        newRoute.setId("dynamic-pay-route");
        // 设置路径断言
        newRoute.getPredicates().add(
                PredicateDefinitionBuilder.path("/pay/**").build()
        );
        // 设置转发URI
        newRoute.setUri(UriDefinitionBuilder.uri("lb://pay-service").build());

        // 写入路由(响应式操作)
        Mono<Void> result = routeDefinitionWriter.save(Mono.just(newRoute));
        result.subscribe(
                success -> {},
                error -> ResponseEntity.badRequest().body("添加路由失败:" + error.getMessage())
        );
        return ResponseEntity.ok("路由添加成功");
    }
}

三、请求 / 响应处理工具类

1. ServerHttpRequestDecorator(修改请求)

场景:重写请求路径、添加自定义请求头

java

运行

复制代码
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import reactor.core.publisher.Mono;

public class ModifyRequestFilter implements GatewayFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 原始请求
        ServerHttpRequest originalRequest = exchange.getRequest();
        
        // 装饰请求:添加机房头 + 重写路径
        ServerHttpRequest modifiedRequest = originalRequest.mutate()
                .header("X-Idc", "shanghai") // 添加机房标识
                .header("X-Request-Id", java.util.UUID.randomUUID().toString()) // 请求ID
                .path("/v2" + originalRequest.getPath()) // 路径重写:/order → /v2/order
                .build();
        
        // 替换exchange中的请求
        ServerWebExchange modifiedExchange = exchange.mutate()
                .request(modifiedRequest)
                .build();
        
        return chain.filter(modifiedExchange);
    }
}

2. ServerHttpResponseDecorator(修改响应)

场景:统一包装响应体、修改响应状态码

java

运行

复制代码
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;

public class ModifyResponseFilter implements GatewayFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();

        // 装饰响应:统一包装JSON返回体
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Flux<? extends DataBuffer> body) {
                // 仅处理JSON响应
                if (originalResponse.getHeaders().getContentType() == MediaType.APPLICATION_JSON) {
                    // 合并响应体为单个Buffer
                    return super.writeWith(body.collectList().flatMap(dataBuffers -> {
                        // 读取原始响应内容
                        StringBuilder responseContent = new StringBuilder();
                        for (DataBuffer buffer : dataBuffers) {
                            byte[] bytes = new byte[buffer.readableByteCount()];
                            buffer.read(bytes);
                            responseContent.append(new String(bytes, StandardCharsets.UTF_8));
                            // 释放缓冲区(避免内存泄漏)
                            org.springframework.core.io.buffer.DataBufferUtils.release(buffer);
                        }

                        // 包装响应体
                        String wrappedResponse = String.format(
                                "{\"code\":200,\"msg\":\"success\",\"data\":%s}",
                                responseContent.toString()
                        );
                        // 构建新的响应Buffer
                        DataBuffer buffer = bufferFactory.wrap(wrappedResponse.getBytes(StandardCharsets.UTF_8));
                        // 设置响应头
                        originalResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                        originalResponse.setStatusCode(HttpStatus.OK);
                        return Mono.just(buffer);
                    }));
                }
                // 非JSON响应直接放行
                return super.writeWith(body);
            }
        };

        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
}

3. DataBufferUtils(处理请求体)

场景:读取完整 POST 请求体、释放缓冲区

java

运行

复制代码
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;

public Mono<Void> readRequestBody(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    
    // 仅处理POST请求
    if (request.getMethod().equals(org.springframework.http.HttpMethod.POST)) {
        // 读取请求体(Flux<DataBuffer> → 完整字符串)
        Mono<String> requestBodyMono = DataBufferUtils.join(request.getBody())
                .map(buffer -> {
                    byte[] bytes = new byte[buffer.readableByteCount()];
                    buffer.read(bytes);
                    // 必须释放缓冲区!
                    DataBufferUtils.release(buffer);
                    return new String(bytes, StandardCharsets.UTF_8);
                });

        return requestBodyMono.flatMap(requestBody -> {
            System.out.println("完整请求体:" + requestBody);
            // 业务处理:如解析JSON、验签等
            return chain.filter(exchange);
        });
    }
    
    return chain.filter(exchange);
}

4. HttpHeadersUtils(提取真实 IP)

场景:处理反向代理 / CDN 场景,提取客户端真实 IP

java

运行

复制代码
import org.springframework.cloud.gateway.support.HttpHeadersUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import java.net.InetSocketAddress;

public String getRealClientIp(ServerWebExchange exchange) {
    HttpHeaders headers = exchange.getRequest().getHeaders();
    InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
    
    // 提取真实IP(优先X-Forwarded-For → X-Real-IP → 远程地址)
    String realIp = HttpHeadersUtils.extractClientIp(headers, remoteAddress);
    
    // 判断Content-Type是否为JSON
    boolean isJson = HttpHeadersUtils.isContentType(headers, org.springframework.http.MediaType.APPLICATION_JSON);
    
    System.out.println("客户端真实IP:" + realIp);
    System.out.println("是否JSON请求:" + isJson);
    
    return realIp;
}

四、过滤器执行工具类

1. GatewayFilterFactory(自定义过滤器)

场景:自定义机房标记过滤器(给请求添加机房头)

java

运行

复制代码
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;

@Component
public class IdcTagGatewayFilterFactory extends AbstractGatewayFilterFactory<IdcTagGatewayFilterFactory.Config> {
    // 配置参数名
    public static final String IDC_KEY = "idc";

    public IdcTagGatewayFilterFactory() {
        super(Config.class);
    }

    // 配置参数顺序(供yml使用)
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList(IDC_KEY);
    }

    // 过滤器逻辑:添加机房标记请求头
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 从配置中获取机房值,添加到请求头
            var modifiedRequest = exchange.getRequest().mutate()
                    .header("X-Idc-Tag", config.getIdc())
                    .build();
            return chain.filter(exchange.mutate().request(modifiedRequest).build());
        };
    }

    // 配置类
    public static class Config {
        private String idc;

        public String getIdc() {
            return idc;
        }

        public void setIdc(String idc) {
            this.idc = idc;
        }
    }
}

// yml配置使用
/*
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          filters:
            - IdcTag=shanghai # 自定义过滤器,参数为上海机房
*/

2. GlobalFilter(全局过滤器)

场景:全局跨机房调用监控

java

运行

复制代码
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class IdcMonitorGlobalFilter implements GlobalFilter, Ordered {
    // 本地机房标识(可从配置中心读取)
    private static final String LOCAL_IDC = "shanghai";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
        // 前置处理:记录请求开始时间
        long startTime = System.currentTimeMillis();
        
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            // 后置处理:计算耗时 + 判断是否跨机房
            long cost = System.currentTimeMillis() - startTime;
            String targetIdc = exchange.getRequest().getHeaders().getFirst("X-Idc-Tag");
            
            if (targetIdc != null && !LOCAL_IDC.equals(targetIdc)) {
                // 跨机房调用,记录日志/告警
                System.out.printf("跨机房调用:本地=%s, 目标=%s, 耗时=%dms%n", 
                        LOCAL_IDC, targetIdc, cost);
            }
        }));
    }

    // 执行顺序:最后执行(LOWEST_PRECEDENCE)
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

五、异常处理工具类

1. ErrorWebExceptionHandler(自定义异常响应)

场景:统一网关异常返回格式(JSON)

java

运行

复制代码
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;

@Component
@Order(-1) // 优先级高于默认异常处理器
public class CustomErrorWebExceptionHandler implements ErrorWebExceptionHandler {
    private final ErrorAttributes errorAttributes;
    private final ServerCodecConfigurer serverCodecConfigurer;
    private final ApplicationContext applicationContext;

    // 构造器注入依赖
    public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes,
                                          ServerCodecConfigurer serverCodecConfigurer,
                                          ApplicationContext applicationContext) {
        this.errorAttributes = errorAttributes;
        this.serverCodecConfigurer = serverCodecConfigurer;
        this.applicationContext = applicationContext;
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        // 构建统一异常响应体
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("code", 500);
        errorResponse.put("msg", "网关处理异常:" + ex.getMessage());
        errorResponse.put("timestamp", System.currentTimeMillis());
        errorResponse.put("path", exchange.getRequest().getPath());

        // 构建JSON响应
        return RouterFunctions.route(RequestPredicates.all(), request ->
                ServerResponse.status(500)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(errorResponse))
        ).route(exchange)
                .flatMap(handler -> handler.handle(exchange))
                .then(Mono.defer(() -> Mono.error(ex)));
    }
}

2. GatewayException(自定义异常)

场景:自定义路由异常并抛出

java

运行

复制代码
import org.springframework.cloud.gateway.support.GatewayException;
import org.springframework.web.server.ServerWebExchange;

// 自定义异常
public class IdcRouteNotFoundException extends GatewayException {
    public IdcRouteNotFoundException(String message) {
        super(message);
    }
}

// 使用示例
public Mono<Void> checkIdcRoute(ServerWebExchange exchange, GatewayFilterChain chain) {
    String idc = exchange.getRequest().getHeaders().getFirst("X-Idc");
    if (idc == null || idc.isEmpty()) {
        // 抛出自定义异常(会被CustomErrorWebExceptionHandler捕获)
        return Mono.error(new IdcRouteNotFoundException("未传递机房标识X-Idc"));
    }
    return chain.filter(exchange);
}

六、其他核心工具类

FilteringWebHandler(扩展过滤器链)

场景:自定义过滤器链执行逻辑

java

运行

复制代码
import org.springframework.cloud.gateway.handler.FilteringWebHandler;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.WebHandler;
import java.util.List;

@Configuration
public class CustomWebHandlerConfig {

    // 自定义FilteringWebHandler,添加额外全局过滤器
    @Bean
    public WebHandler customWebHandler(List<GlobalFilter> globalFilters) {
        // 添加自定义全局过滤器到链首
        GlobalFilter firstFilter = (exchange, chain) -> {
            System.out.println("自定义过滤器链:第一个执行");
            return chain.filter(exchange);
        };
        globalFilters.add(0, firstFilter);
        
        // 创建自定义FilteringWebHandler
        FilteringWebHandler handler = new FilteringWebHandler(globalFilters);
        return handler;
    }
}

关键说明

  1. 依赖准备:确保项目引入核心依赖

xml

复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>3.1.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
  1. 响应式编程约束 :所有操作避免使用 block(),遵循 Reactor 异步规则;
  2. 资源释放 :操作 DataBuffer 后必须调用 DataBufferUtils.release()
  3. 配置生效 :自定义 RoutePredicateFactory/GatewayFilterFactory 需添加 @Component,且类名后缀必须为 RoutePredicateFactory/GatewayFilterFactory
相关推荐
bigdata-rookie1 天前
数据仓库建模
大数据·分布式·spark
码以致用1 天前
Kafka笔记
笔记·分布式·kafka
回家路上绕了弯1 天前
Vavr 工具实用指南:Java 函数式编程的高效落地方案
分布式·后端
BullSmall2 天前
Kafka单机与集群部署全攻略
分布式·kafka
隐语SecretFlow2 天前
如何在 Kuscia 上运行 SCQL 联合分析任务
分布式·安全·架构·开源
少许极端2 天前
Redis入门指南:从零到分布式缓存-hash与list类型
redis·分布式·缓存·list·hash
jiayong232 天前
RabbitMQ 完全指南
分布式·rabbitmq
BullSmall2 天前
JDK17下Kafka部署全指南
分布式·kafka
BullSmall2 天前
MinIO分布式存储实战指南
分布式