【深入解析spring cloud gateway】11 用最简单的方式修改gateway响应报文

gateway修改响应报文,也不是一件容易的事,我们来看下如何简单地来修改返回报文

一、官方示例

先看看官方示例,如何修改一个响应报文

java 复制代码
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyResponseBody(String.class, String.class,
                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))
        .build();
}

这种方式在定义Route的地方写,不太灵活。这种方式本质上就是调用下面的代码,实现的返回报文重写

java 复制代码
	public <T, R> GatewayFilterSpec modifyResponseBody(Class<T> inClass, Class<R> outClass,
			RewriteFunction<T, R> rewriteFunction) {
		return filter(getBean(ModifyResponseBodyGatewayFilterFactory.class)
				.apply(c -> c.setRewriteFunction(inClass, outClass, rewriteFunction)));
	}

其中最关键的就是ModifyResponseBodyGatewayFilterFactory了!

其中重写的逻辑,就是RewriteFunction了。里面就是我们重写返回报文的逻辑了。

重写报文的核心实现就是下面的代码了ModifyResponseBodyGatewayFilterFactory.writeWith,可以看出来,好像也不简单啊,如果我们想要重写返回报文,是可以参考下面的代码。

java 复制代码
		public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

			Class inClass = config.getInClass();
			Class outClass = config.getOutClass();

			String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
			HttpHeaders httpHeaders = new HttpHeaders();
			// explicitly add it in this way instead of
			// 'httpHeaders.setContentType(originalResponseContentType)'
			// this will prevent exception in case of using non-standard media
			// types like "Content-Type: image"
			httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType);

			ClientResponse clientResponse = prepareClientResponse(body, httpHeaders);

			// TODO: flux or mono
			Mono modifiedBody = extractBody(exchange, clientResponse, inClass)
					.flatMap(originalBody -> config.getRewriteFunction().apply(exchange, originalBody))
					.switchIfEmpty(Mono.defer(() -> (Mono) config.getRewriteFunction().apply(exchange, null)));

			BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
			CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange,
					exchange.getResponse().getHeaders());
			return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
				Mono<DataBuffer> messageBody = writeBody(getDelegate(), outputMessage, outClass);
				HttpHeaders headers = getDelegate().getHeaders();
				if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)
						|| headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
					messageBody = messageBody.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
				}
				// TODO: fail if isStreamingMediaType?
				return getDelegate().writeWith(messageBody);
			}));
		}

二、自定义修改报文实现

想要自己修改返回报文,那是不是直接复制上面的ModifyResponseBodyGatewayFilterFactory.writeWith就可以了?这样好像也太复杂,里面的操作流、缓存的代码让人看不太明白。那有没有简单的办法呢?有的,那就是直接把我们修改报文委派给ModifyResponseBodyGatewayFilterFactory,我们只提供一个RewriteFunction的业务实现,就可以了。

2.1 微服务hello-service定义的后端接口如下:

java 复制代码
@Controller
@Slf4j
public class ModifyBodyController {
    @RequestMapping(value = "/modify-response/hello", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> modifyResponse(@RequestBody Map<String, String> map) {
        return map;
    }
}

2.2 定义自己的Filter Bean

java 复制代码
    @Bean
    public ModifyResponseBodyFilter modifyResponseBodyFilter(
            ServerCodecConfigurer codecConfigurer, Set<MessageBodyDecoder> bodyDecoders,
            Set<MessageBodyEncoder> bodyEncoders) {
        return new ModifyResponseBodyFilter(bodyDecoders, bodyEncoders, codecConfigurer.getReaders());
    }

这段代码,实际上参考了GatewayAutoConfiguration里面对ModifyResponseBodyGatewayFilterFactory的bean的定义。ModifyResponseBodyGatewayFilterFactory需要一系列的编码、解码工具,从容器中注入进来。

三、修改响应报文具体实现

gateway定义一个GlobalFilter,修改响应报文,将原来返回的Map中,添加一行数据【"hello": "new response body insert!!"】

注意order需要定义为NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1,即在WRITE_RESPONSE_FILTER_ORDER之前。

java 复制代码
public class ModifyResponseBodyFilter implements GlobalFilter, Ordered {
    private final Gson gson = new Gson();
    private final  Set<MessageBodyDecoder> messageBodyDecoders;

    private final Set<MessageBodyEncoder> messageBodyEncoders;

    private final List<HttpMessageReader<?>> messageReaders;

    public ModifyResponseBodyFilter(Set<MessageBodyDecoder> messageBodyDecoders, Set<MessageBodyEncoder> messageBodyEncoders, List<HttpMessageReader<?>> messageReaders) {
        this.messageBodyDecoders = messageBodyDecoders;
        this.messageBodyEncoders = messageBodyEncoders;
        this.messageReaders = messageReaders;
    }


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ModifyResponseBodyGatewayFilterFactory.Config config = new ModifyResponseBodyGatewayFilterFactory.Config();
        config.setInClass(String.class);
        config.setOutClass(String.class);
        config.setRewriteFunction(new RewriteFunction() {
            @Override
            public Object apply(Object o, Object o2) {
                ServerWebExchange serverWebExchange = (ServerWebExchange) o;
                String oldBody = (String) o2;
                if (exchange.getRequest().getURI().getRawPath().contains("modify-response")) {
                    Map map = gson.fromJson(oldBody, Map.class);
                    map.put("hello", "new response body insert!!");
                    return Mono.just(gson.toJson(map));
                }
                return Mono.just(oldBody);
            }
        });
        return new ModifyResponseBodyGatewayFilterFactory(messageReaders,messageBodyDecoders,messageBodyEncoders)
                .apply(config).filter(exchange,chain);
    }

    @Override
    public int getOrder() {
        return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
    }
}

测试接口

bash 复制代码
curl --location 'http://localhost:8080/hello-service/modify-response/hello' \
--header 'Content-Type: application/json' \
--data '{"aaa":"bbb"}'

返回报文如下

json 复制代码
{
    "aaa": "bbb",
    "hello": "new response body insert!!"
}

可以看到,我们在原有报文的基础上,插入了【 "hello": "new response body insert!!"】

本示例源码链接:https://gitee.com/syk1234/spring-cloud-new-demo.git

相关推荐
牛角上的男孩20 小时前
Istio Gateway发布服务
云原生·gateway·istio
柳叶寒3 天前
医院信息化与智能化系统(17)
java·nacos·gateway·全栈·项目
cyt涛3 天前
SpringCloudGateway — 网关路由
java·开发语言·网关·gateway·路由·断言·转发
代码魔法师Sunny5 天前
502 Bad Gateway 错误详解:从表现推测原因,逐步排查直至解决
gateway
RedCong6 天前
通过route访问Openshift上的HTTP request报错504 Gateway Time-out【已解决】
http·gateway·openshift
飞升不如收破烂~7 天前
包括 Nginx、Gateway、Nacos、Dubbo、Sentinel、RocketMQ 和 Seata 的调用链路描述:
nginx·gateway·dubbo
还是转转7 天前
Kong Gateway 指南
gateway·kong
攒了一袋星辰12 天前
微服务网关gateway过滤器工厂与自定义过滤器
微服务·架构·gateway
猫猫不是喵喵.13 天前
Gateway 统一网关
java·网络·微服务·gateway
13 天前
504 Gateway Time-outopenresty
gateway