springcloud-gateway网关转换响应数据结构乱码问题处理

本文主要是介绍springcloud-gateway网关转换响应数据结构乱码问题处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考文章:记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码_springcloud gateway response 响应zip响应乱码-CSDN博客

在上述文章记录请求的headers中携带Accept-Encoding参数乱码问题,主要是字节流被压缩未解压直接转字符导致的,因此需要对字节先解压再转换,最终效果实现代码如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.HttpHeaders;
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.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.base.Throwables;import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;@Component
public class CustomConvertResponseFilter implements GlobalFilter, Ordered {public static final Logger logger = LoggerFactory.getLogger(CustomConvertResponseFilter.class);private final static String COVERT_FLAG = "gateway-Covert";@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpResponse response = exchange.getResponse();String trackId = UUID.randomUUID().toString();ServerHttpResponseDecorator responseDecorated = new ServerHttpResponseDecorator(response) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {if (body instanceof Flux) {Flux<? extends DataBuffer> fluxBody = Flux.from(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 bodyStr = "";boolean gzip = false;HttpHeaders headers = response.getHeaders();List<String> contentEncoding = headers.get("Content-Encoding");if (CollectionUtils.isNotEmpty(contentEncoding) && contentEncoding.contains("gzip")) {gzip = true;}String contentType = headers.getFirst("Content-Type");if (StringUtils.isBlank(contentType) || !contentType.contains("application/json")) {response.getHeaders().add(COVERT_FLAG,"0");return bufferFactory().wrap(content);}try {bodyStr = unzipByte2String(content,gzip);JSONObject jsonObject = JSON.parseObject(bodyStr);jsonObject.put("trackId", trackId);bodyStr = JSON.toJSONString(jsonObject, SerializerFeature.WriteMapNullValue);byte[] respData = string2ZipByte(bodyStr,gzip);getDelegate().getHeaders().setContentLength(respData.length);response.getHeaders().add(COVERT_FLAG,"1");return bufferFactory().wrap(respData);} catch (Exception e) {logger.error("Gateway.CustomConvertResponseFilter发生异常,异常信息:{}", Throwables.getStackTraceAsString(e));}response.getHeaders().add(COVERT_FLAG,"0");return bufferFactory().wrap(content);}));}return super.writeWith(body);}};return chain.filter(exchange.mutate().response(responseDecorated).build());}@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE + 10199;}/*** gzip压缩后的字节转String* @param compressed* @return* @throws IOException*/public static String unzipByte2String(byte[] compressed,boolean gzip) throws IOException {if(!gzip){return new String(compressed, StandardCharsets.UTF_8);}// 使用GZIPInputStream解压缩数据ByteArrayInputStream bais = new ByteArrayInputStream(compressed);GZIPInputStream gzis = new GZIPInputStream(bais);ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;while ((len = gzis.read(buffer)) != -1) {baos.write(buffer, 0, len);}// 获取解压缩后的字节数组byte[] decompressedData = baos.toByteArray();// 将字节数组转换为字符串(假设内容是UTF-8编码的文本)return new String(decompressedData, StandardCharsets.UTF_8);}/*** String转gzip压缩后的字节* @param json* @return* @throws IOException*/public static byte[] string2ZipByte(String json,boolean gzip) throws IOException {// 将字符串转换为字节数组(使用UTF-8编码)byte[] stringBytes = json.getBytes(StandardCharsets.UTF_8);if(!gzip){return stringBytes;}// 创建一个ByteArrayOutputStream来存储压缩后的数据ByteArrayOutputStream baos = new ByteArrayOutputStream();// 创建一个GZIPOutputStream来压缩数据并写入到baos中GZIPOutputStream gos = new GZIPOutputStream(baos);// 写入字符串的字节到GZIPOutputStream中gos.write(stringBytes);// 完成写入后,关闭GZIPOutputStream以完成压缩过程gos.close();// 获取压缩后的字节数组return baos.toByteArray();}
}

这篇关于springcloud-gateway网关转换响应数据结构乱码问题处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/944157

相关文章

原地去重问题和合并有序数组问题

原地去重问题 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。这是leetcode上的一道题 这里我们用两个指针来计算去重后的元素个数 dst是用来记录去重后有多少值以及给他们赋值 src则是来检查重复的值,将值赋给dst 代码表示

中断处理的工作队列机制

工作队列(work queue)是另外一种将工作推后执行的形式 ,它和我们前面讨论的所有其他形式都有不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择

中断处理的 tasklet 机制

中断处理的 tasklet 机制中断服务程序一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化。但是,中断是一个随机事件,它随时会到来,如果关中断的时间太长,CPU就不能及时响应其他的中断请求,从而造成中断的丢失。因此,Linux内核的目标就是尽可能快的处理完中断请求,尽其所能把更多的处理向后推迟。例如,假设一个数据块已经达到了网线,当中断控制器接受到这个中断请求信号时,Lin

javaEE进阶——SpringBoot与SpringMVC第一讲

文章目录 什么是springMVCSpringMVC什么是模型、视图、控制器MVC和SpringMVC的关系SpringMVC的使用第一个SpringMVC程序@RestController什么是注解 那么@RestController到底是干嘛的呢?@RequestMapping 如何接收来自请求中的querystrying@RequestParam@RequestMapping("/m

【JavaEE进阶】 Bean的作用域与生命周期

文章目录 🍃Bean的作用域🚩作用域的使用🚩观察Bean的作用域🎈单例作用域🎈多例作用域🎈请求作用域🎈会话作⽤域🎈Application作⽤域 🎄Bean的⽣命周期⭕总结 🍃Bean的作用域 Bean的作⽤域是指Bean在Spring框架中的某种⾏为模式. 比如单例作⽤域:表⽰ Bean 在整个Spring 中只有⼀份, 它是全局共享的.那么当其他⼈修改了

Rust中使用Rocket框架返回html网页,返回一个基于 Handlebars (HBS) 模板的响应

在Rust中使用Rocket框架返回网页,通常涉及创建一个路由,该路由将返回一个HTML页面。Rocket是一个快速、易用且可扩展的Web框架,它允许你以一种简洁的方式定义路由和处理请求。 一、使用Rocket框架返回一个简单的HTML页面: 添加依赖:在你的Cargo.toml文件中添加Rocket框架和相关的依赖。 [dependencies]rocket = "0.5.0" 创建

JAVA SE 学习笔记-第12节 方法复习

1_1_12_08_复习简单方法的使用.flv 1_1_12_09_方法的定义格式.flv 1_1_12_10_方法的三种调用格式.flv 1_1_12_11_方法的调用流程图解.flv 1_1_12_12_对比有参数和无参数.flv 1_1_12_13_对比有返回值和无返回值.flv 1_1_12_14_方法练习1_比较两个数字.flv 1_1_12_15_方法练习

JAVA SE 学习笔记-第11节 开发工具-IDEA

1_1_11_01_集成开发环境IDE的概述.flv 1_1_11_02_IntelliJ-IDEA的安装.flv 添加链接描述 因为教程用的是Version 2017.3 因此选择Version 2017.3 2017.3.7 for Linux (tar.gz) 安装时注意以下: 1_1_11_03_IDEA的项目结构.flv 1_1_11_04_IDEA的Hello

JAVA SE 学习笔记-第8节 JDK9新特性-Jshell

1_1_8_15_JDK9的JShell简单使用.flv 进入jshell环境,直接输入jshell便进入 退出时为 /exit 1_1_8_16_编译器的两点优化.flv

JAVA SE 学习笔记-第7节 方法入门

1_1_7_12_方法入门_概念引入.flv 1_1_7_13_方法入门_方法的定义.flv 1_1_7_14_方法入门_方法的调用.flv