Spring Cloud 网关在过滤器中发送响应

Posted

技术标签:

【中文标题】Spring Cloud 网关在过滤器中发送响应【英文标题】:Spring Cloud gateway send response in filter 【发布时间】:2020-04-28 21:12:42 【问题描述】:

我使用 Spring Cloud Gateway 作为边缘服务器。 这是流程

如果请求具有名为“x-foo”的标头,则查找标头值,从另一台服务器获取字符串并将该字符串作为响应发送,而不是实际代理请求。

这是过滤器 DSL 的代码

@Bean
    public RouteLocator routes(RouteLocatorBuilder builder) 
        return builder.routes()
                .route("foo-filter", r -> r.header('x-foo').and().header("x-intercepted").negate()
                        .filters(f -> f.filter(fooFilter))
                        .uri("http://localhost:8081")) // 8081 is self port, there are other proxy related configurations too
                .build();
    

Foo 过滤器的代码

@Component
@Slf4j
public class FooFilter implements GatewayFilter 

    @Autowired
    private ReactiveRedisOperations<String, String> redisOps;

    @Value("$header-name")
    private String headerName;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
        ServerHttpRequest request = exchange.getRequest();
        var foo = request.getHeaders().getFirst(headerName);

        return redisOps.opsForHash()
                .get("foo:" + foo, "response")
                .doOnSuccess(s -> 
                    log.info("data on success");
                    log.info(s.toString()); // I am getting proper response here
                    if (s != null) 
                        ServerHttpResponse response = exchange.getResponse();
                        response.setStatusCode(HttpStatus.OK);
                        response.getHeaders().set("x-intercepted", "true");

                        byte[] bytes = s.toString().getBytes(StandardCharsets.UTF_8);

                        DataBuffer buffer = response.bufferFactory().wrap(bytes);

                        response.writeWith(Mono.just(buffer));

                        response.setComplete();
                    
                )
                .then(chain.filter(exchange));
    


问题是,响应得到正确的 200 代码,注入的标头存在于响应中,但响应中的数据不可用。

【问题讨论】:

此answer 似乎与您的问题有关。 正是我需要的。 但是这在返回后给了我错误。 java.lang.UnsupportedOperationException: null at org.springframework.http.ReadOnlyHttpHeaders.putAll(ReadOnlyHttpHeaders.java:131) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE] 抑制:reactor.core。 publisher.FluxOnAssembly$OnAssemblyException:在以下站点观察到错误:|_ 检查点? org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain] |_ checkpoint ? org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain] 嗨@Akshay,请与stacktrace分享更新的代码 显然我用过switchIfEmpty,而且是提前执行。 【参考方案1】:

这就是我开始工作的方式。

使用flatMap 而不是doOnSuccess 不要使用thenswitchIfEmpty 而是使用onErrorResume 返回response.writeWith
    @Component
    @Slf4j
    public class FooFilter implements GatewayFilter 
    
        @Autowired
        private ReactiveRedisOperations<String, String> redisOps;
    
        @Value("$header-name")
        private String headerName;
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
            ServerHttpRequest request = exchange.getRequest();
            var foo = request.getHeaders().getFirst(headerName);
    
            return redisOps.opsForHash()
                    .get("foo:" + foo, "response")
                    .flatMap(s -> 
                        log.info("data on success");
                        log.info(s.toString()); // I am getting proper response here
                        if (s != null) 
                            ServerHttpResponse response = exchange.getResponse();
                            response.setStatusCode(HttpStatus.OK);
                            response.getHeaders().set("x-intercepted", "true");
    
                            byte[] bytes = s.toString().getBytes(StandardCharsets.UTF_8);
    
                            DataBuffer buffer = response.bufferFactory().wrap(bytes);
    
                            return response.writeWith(Mono.just(buffer));
   
                        else return chain.filter(exchange).then(Mono.fromRunnable(() -> log.info("It was empty") 
                    )
                    .onErrorResume(chain.filter(exchange));
        
    

【讨论】:

以上是关于Spring Cloud 网关在过滤器中发送响应的主要内容,如果未能解决你的问题,请参考以下文章

微服务下使用网关 Spring Cloud Gateway

又被面试官装到了:Spring Cloud Gateway 网关限流怎么做?

Spring Cloud-Gateway网关

万字Spring Cloud Gateway2.0,面向未来的技术,了解一下?

Spring Cloud Gateway - 快速开始

Hystrix断路器在微服务网关中的应用(Spring Cloud Gateway)