Spring Cloud Gateway 拦截响应问题分析(数据截断问题)

 更新时间:2023年01月07日 14:36:10   作者:起风哥  
这篇文章主要介绍了Spring Cloud Gateway 拦截响应问题分析(数据截断问题),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

Spring Cloud Gateway是Spring 官方基于Spring 5.0、Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单有效的、统一的 API 路由管理方式。

Spring Cloud Gateway 作为 Spring Cloud 生态系中的网关,其目标是替代 Netflix Zuul,它不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,如:安全、监控/埋点和限流等。

Spring Cloud Gateway依赖Spring Boot和Spring WebFlux,基于Netty 运行。不能在传统的 servlet 容器中工作也不能构建成war包。

关于Spring Cloud Gateway 核心概念

1、Route

Route 是网关的基础元素,由 ID、目标 URI、断言、过滤器组成。当请求到达网关时,由 Gateway HandlerMapping 通过断言进行路由匹配(Mapping),断言为真时匹配到路由。

2、Predicate

Predicate 是 Java 8 中提供的一个函数。输入类型是 Spring Framework ServerWebExchange。它允许开发人员匹配来自 HTTP 的请求,例如请求头或者请求参数。简单来说它就是匹配条件。

3、Filter

Filter是Gateway 中的过滤器,可以在请求发出前后进行一些业务上的处理。

Spring Cloud Gateway 拦截响应

最近因为上链路追踪后发现如果在服务层将异常拦截掉,在链路追踪界面上就不会显示异常链路信息,除了服务异常意外,系统的异常不会触发链路error,所以对服务层做了个变更,将所有未处理异常直接捕获后统一封装完抛出,这个时候就需要在网关层统一处理那么网关需要对响应数据进行拦截如果是 9999错误码,则封装后返回,如果是其它响应码或者其它数据直接返回。

起初写法测试后发现数据过长时被截断。

 return super.writeWith(fluxBody.map(dataBuffer -> {
	
 
	byte[] content = new byte[dataBuffer.readableByteCount()];
	
	dataBuffer.read(content);
	// 释放掉内存
	DataBufferUtils.release(dataBuffer);
	String str = new String(content, Charset.forName("UTF-8"));
 
	originalResponse.getHeaders().setContentLength(str.getBytes().length);
	log.error("gateway catch service exception error:"+ str);
	
	JsonResult result = new JsonResult();
	result.setCode(ErrorCode.SYS_EXCEPTION.getCode());
	result.setMessage(ErrorCode.SYS_EXCEPTION.getMsg());
	
	return bufferFactory.wrap(str.getBytes());
}));                   

查询api后发现存在一个DataBufferFactory可以一次性join完所有数据后拼接就不会产生截断问题。

DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();

于是修改代码如下

package com.server.gateway.filters;
 
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicReference;
 
import org.apache.http.protocol.HTTP;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
 
import com.framework.common.enums.ErrorCode;
import com.framework.common.web.JsonResult;
 
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
 
@Component
@Slf4j
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered{
 
	@Override
	public int getOrder() {
		// -1 is response write filter, must be called before that
		return -2;
	}
 
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpResponse originalResponse = exchange.getResponse();
		DataBufferFactory bufferFactory = originalResponse.bufferFactory();
		ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
			@Override
			public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
				AtomicReference<String> bodyRef = new AtomicReference<>();
				if (body instanceof Flux) {
					Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
					
					return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
						
						DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
						DataBuffer join = dataBufferFactory.join(dataBuffers);
						
						byte[] content = new byte[join.readableByteCount()];
						
						join.read(content);
						// 释放掉内存
						DataBufferUtils.release(join);
						String str = new String(content, Charset.forName("UTF-8"));
		
						originalResponse.getHeaders().setContentLength(str.getBytes().length);
						log.error("gateway catch service exception error:"+ str);
						
						JsonResult result = new JsonResult();
				        result.setCode(ErrorCode.SYS_EXCEPTION.getCode());
				        result.setMessage(ErrorCode.SYS_EXCEPTION.getMsg());
				        
						return bufferFactory.wrap(str.getBytes());
					}));
					
				}
				// if body is not a flux. never got there.
	                return super.writeWith(body);
	            }
	        };
	        // replace response with decorator
	        return chain.filter(exchange.mutate().response(decoratedResponse).build());
	    }
 
}

经过如上修改后链路追踪可以实现哪个服务出错就立马出现链路异常马上可以定位到哪个服务出现了未处理异常

到此这篇关于Spring Cloud Gateway 拦截响应(数据截断问题)的文章就介绍到这了,更多相关Spring Cloud Gateway 拦截响应内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java list利用遍历进行删除操作3种方法解析

    Java list利用遍历进行删除操作3种方法解析

    这篇文章主要介绍了Java list利用遍历进行删除操作3种方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • JavaWeb项目中JSP访问的问题解决

    JavaWeb项目中JSP访问的问题解决

    JSP文件一般有两个存放位置,本文主要介绍了JavaWeb项目中JSP访问的问题解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • java实现快速排序图文详解

    java实现快速排序图文详解

    网上关于快速排序的算法原理和算法实现都比较多,不过java是实现并不多,而且部分实现很难理解,和思路有点不搭调。所以整理了这篇文章。如果有不妥之处还请建议
    2021-08-08
  • 如何更快乐的使用Java 8中的Lambda特性

    如何更快乐的使用Java 8中的Lambda特性

    从java8出现以来lambda是最重要的特性之一,它可以让我们用简洁流畅的代码完成一个功能。下面这篇文章主要给大家介绍了关于如何更快乐的使用Java 8中的Lambda特性的相关资料,需要的朋友可以参考下
    2018-11-11
  • 自己动手写的mybatis分页插件(极其简单好用)

    自己动手写的mybatis分页插件(极其简单好用)

    最近做了个项目,需要用到mybatis分页功能,网上找了很多插件,都不太合适,于是就自己动手写了个mybatis分页插件功能,非常不错,代码简单易懂,需要的朋友参考下吧
    2016-11-11
  • Java遍历文件夹下所有文件并重新命名

    Java遍历文件夹下所有文件并重新命名

    这篇文章主要为大家详细介绍了Java遍历文件夹下所有文件并重新命名,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • Java快速排序与归并排序及基数排序图解示例

    Java快速排序与归并排序及基数排序图解示例

    快速排序是基于二分的思想,对冒泡排序的一种改进。主要思想是确立一个基数,将小于基数的数放到基数左边,大于基数的数字放到基数的右边,然后在对这两部分进一步排序,从而实现对数组的排序
    2022-09-09
  • Java设计模式的事件模型详解

    Java设计模式的事件模型详解

    这篇文章主要为大家详细介绍了Java设计模式的事件模型,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • SpringMVC 单文件上传与多文件上传实例

    SpringMVC 单文件上传与多文件上传实例

    这篇文章主要介绍了SpringMVC 单文件上传与多文件上传实例的相关资料,需要的朋友可以参考下
    2017-06-06
  • Java初学者常问的问题(推荐)

    Java初学者常问的问题(推荐)

    本文介绍一些Java初学者常问的问题,很多朋友对可以用%除以一个小数吗? a += b 和 a = a + b 的效果有区别吗? 声明一个数组为什么需要花费大量时间? 为什么Java库不用随机pivot方式的快速排序?等等一系列问题有疑惑,下面就通过本文给大家详细介绍下
    2017-03-03

最新评论