场景: 在一个独立的网关服务中,对部分符合业务条件的请求,设置一个header
错误代码
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() { return 0; }
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders mutableHeaders = HttpHeaders.writableHttpHeaders(request.getHeaders());
// 设置请求头
mutableHeaders.set("DESC", "NOTHING HAPPENED");
// 使用可变的头创建新的请求
ServerHttpRequest mutatedRequest = request.mutate()
.headers(httpHeaders -> httpHeaders.addAll(mutableHeaders))
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
}
上面这段代码,乍一看没啥问题,而且运行起来也ok。然而在发送POST请求时出现了问题,请求body为空,于是直接给前端甩了400错误。
以往SpringBoot的项目都是在过滤器中,使用headerMapRequestWrapper
,塞一个header进去就完事。不过在Spring Cloud Gateway这里就是完全不同的路子了,首先全局过滤器中(GlobalFilter
),需要重写这个接口:
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
这其中,可以通过exchange.getRequest()
获得到请求对象。不过得到的请求对象ServerHttpRequest
是只读的,无法直接修改。
于是经过AI工具指导,一步步取出request,headers,塞入指定header后,重新构建request和exchange。
最后成功的出现了问题。。。
解决
其实仿照以往ServletHttpRequest
的处理方式不太对。查阅文档后可以发现,虽然exchange.getRequest()
得到的是只读对象,但是提供了mutate()方法,允许进行修改请求内容。
既然这样,那就没必要一层层取出请求头,再一层层包装回去构建新的请求。直接修改就行了。
正确打开方式如下:
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() { return 0; }
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
return chain.filter(
exchange.mutate().request(request.mutate()
.header("DESC", "NOTHING HAPPENED")
.build())
.build());
}
}