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());
	    }

}

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

 

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Cloud Gateway提供了一种全局响应处理的机制,可以在所有路由处理完成后进行统一的响应处理。具体实现方式是通过实现`GlobalFilter`接口来实现全局过滤器,并在其中处理响应。以下是一个简单的示例代码: ```java @Component public class CustomGlobalFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).then(Mono.fromRunnable(() -> { ServerHttpResponse response = exchange.getResponse(); HttpStatus statusCode = response.getStatusCode(); if (statusCode == HttpStatus.UNAUTHORIZED) { response.getHeaders().setContentType(MediaType.APPLICATION_JSON); String body = "{\"message\": \"Unauthorized\"}"; DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); return response.writeWith(Flux.just(buffer)); } })); } } ``` 在上述代码中,我们通过实现`GlobalFilter`接口并重写`filter`方法,在方法中调用`chain.filter(exchange)`代表继续执行后续的过滤器和路由处理。在其后面通过`then`方法调用`Mono.fromRunnable`,并在其中处理响应。例如,如果响应状态码为`UNAUTHORIZED`,我们可以设置响应头和响应体,然后将其写入响应流中。 需要注意的是,在全局过滤器中处理响应时,需要调用`ServerHttpResponse.writeWith`或`ServerHttpResponse.writeAndFlushWith`方法来写入响应流,而不能使用`ServerHttpResponse.getBody`方法来直接写入响应体。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值