如何在提交之前修改 Spring Cloud Gateway 中的响应正文

Posted

技术标签:

【中文标题】如何在提交之前修改 Spring Cloud Gateway 中的响应正文【英文标题】:How to modify the response body in Spring Cloud Gateway just before the commit 【发布时间】:2018-10-10 14:55:33 【问题描述】:

我在一个项目中使用带有 Spring 5、Spring Reactor 和 Netty 的 Spring Cloud Gateway。对于发送到网关的每个请求,我想在响应发送到客户端之前做一些事情。我发现最好的方法是使用 beforeCommit 方法向响应添加一个操作。

我首先尝试了这种方法:

        exchange.getResponse().beforeCommit(() -> 
            ServerHttpResponse response = exchange.getResponse();
            try 
                myActionDoneHere();
                response.setStatusCode(OK);
                return Mono.empty();
             catch (Exception ex) 
                return Mono.error(new MyException(ex));
            
        );

并尝试在异常处理程序中处理异常:

public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) 
    if (isMyException(ex)) 
     exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        exchange.getResponse().getHeaders().setContentLength(MSG.length());
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(MSG.getBytes())));
    

    return Mono.error(ex);

当我这样做时,当我尝试修改内容长度时会出现异常。如果我了解情况。我不能再在我的处理程序中修改响应,因为它已经提交了。所以我尝试了另一种解决方案,并尝试在提交之前修改我执行的操作中的响应:

exchange.getResponse().beforeCommit(() -> 
    ServerHttpResponse response = exchange.getResponse();
    try 
        myActionDoneHere();
        response.setStatusCode(OK);
        return Mono.empty();
     catch (Exception ex) 
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        response.getHeaders().setContentLength(MSG.length());
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(MSG.getBytes())));
    
);

这次我没有任何异常,我可以修改内容长度但我不能修改响应的正文。

那么有人知道是否可以这样做以及如何做吗?

【问题讨论】:

我使用调试器试图了解正在发生的事情,似乎正文是由最后一个过滤器 NettyWriteResponseFilter 编写的。所以就在提交之前已经太晚了,身体不再可用。但令人困惑的是我仍然可以访问标题。因此,如果有人有一个想法并且知道如何运行一段可以在响应被验证并发送给客户端之前修改响应的代码,我真的很想知道。 可能类似于***.com/questions/48491098/…,但答案没有帮助,因为它只处理异常 【参考方案1】:

我使用了WebClientWriteResponseFilter里面的技术

    if (failedAuthorization) 
        ServerWebExchangeUtils.setResponseStatus(exchange, HttpStatus.UNAUTHORIZED);
        ServerWebExchangeUtils.setAlreadyRouted(exchange);
        final Map<String, String> error = Map.of("error", "unauthorized");
        return chain.filter(exchange).then(Mono.defer(() -> 
            final ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8.toString());
            return response.writeWith(new Jackson2JsonEncoder().encode(Mono.just(error),
                response.bufferFactory(),
                ResolvableType.forInstance(error),
                MediaType.APPLICATION_JSON_UTF8,
                Hints.from(Hints.LOG_PREFIX_HINT, exchange.getLogPrefix()))
            );
        ));
    

【讨论】:

【参考方案2】:

Spring Cloud 网关现在有过滤器来修改请求正文和响应正文。 ModifyResponseBodyModifyRequestBody

【讨论】:

如您链接的文档中所述,这只能使用 Java DSL 配置(即不能使用 YAML)。

以上是关于如何在提交之前修改 Spring Cloud Gateway 中的响应正文的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud项目如何防止重复提交,防重复提交幂等校验,Redis+aop+自定义Annotation实现接口

Spring-cloud 注册服务提供者搭建方法

聊聊spring cloud gateway的NettyConfiguration

Spring Cloud 系列之 Config 配置中心

Spring Cloud 系列之 Config 配置中心

Spring Cloud Alibaba基础教程:Sentinel Dashboard同步Apollo存储规则