教程简介

最近在使用SpringCloud Gateway代理业务接口的是出现了响应数据乱码的情况,业务接口有点特殊,是通过OpenResty代理过一次后的对外链接,问题显示如下:

发现问题

然后通过排除法,不使用OpenResty的业务接口则是正常的,然后对比了一下响应头信息发现如下

发现重复头信息

那么会不会和这些重复的头信息有关系呢?所以针对发现的问题继续了解

理解Accept,Vary

之前对于请求头里面的参数没有特别注意过,此次在排查问题过程中特意了解了下Accept和Vary的作用。

Accept和Vary是成对出现的,大概意思就是客户端(比如说浏览器或者手机)与服务器之间内容协商的头部关键字信息,一个告诉服务器我要啥,一个告诉客户端我给了啥,一应一答,最终达到客户端需要什么信息服务器就给客户端相应要求的信息,完成一种专业术语叫内容协商的机制。

Accept呢还能具体分为如下几种

1
2
3
4
Accept: 声明客户端可以处理的资源格式
Accept-Charset: 声明客户端可以处理的字符集类型
Accept-Language: 声明客户端可以理解的自然语言
Accept-Encoding: 声明客户端支持的编码格式

而服务端涉及的常见头部包括:

1
2
3
Content-Type: 指示资源的 MIME 类型
Content-Language: 指示该资源所期望的自然语言
Content-Encoding: 指示资源使用该编码格式进行内容转换

大家感兴趣的话可以查看下自己浏览器那些请求和响应,按照这个规律来看看。

理解SpringCloud Gateway Header的内部原理

为了更好的排查此问题和根治此问题,所以特意再去了解了下SpringCloud Gateway内部对于Header的内部处理。

SpringCloudGateway请求流程.jpg

如何解决

通过观察不通过SpringCloud Gateway直接去访问的请求头和响应头信息如下
非代理请求头
非代理响应头

然后通过SpringCloud Gateway去访问的请求头和响应头信息如下(通过抓包查看网关内部如何去调用业务接口,命令:tcpdump -i any -s 0 -w 1.pcap)得到文件后使用WireShark打开,如下结果
代理请求头
代理内部响应头
代理外部响应头

很明显,在SpringCloud Gateway内部拿到业务接口响应后,对外的响应处理过程中,头部信息进行了重复,这个重复就是罪魁祸首,所以我们需要解决重复问题即可,具体如下:

在SpringCloud Gateway框架中有一个去除重复头的过滤器DedupeResponseHeaderGatewayFilterFactory该类提供了3种策略处理重复头问题

1
2
3
RETAIN_FIRST: 默认值,保留第一个值
RETAIN_LAST: 保留最后一个值
RETAIN_UNIQUE: 保留所有唯一值,以它们第一次出现的顺序保留

所以在我们的项目配置文件添加如下一段配置
spring.cloud.gateway.default-filters[0]=DedupeResponseHeader=access-control-allow-credentials access-control-allow-methods set-cookie vary content-encoding, RETAIN_FIRST
将重复的几项写入空格分开,最后以 逗号后面跟一个策略保留值即可。

通过以上的配置再次去调用接口,就正常了,其它接口也是正常的。