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
不要使用then
或switchIfEmpty
而是使用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 网关限流怎么做?