SpringBoot2.3.9 乱码问题分析解决

由于业务需求,我们需要将我们原本的SpringBoot2.0.4版本升级到SpringBoot2.3.9版本,经过我们不懈努力,我们基础库适配了SpringBoot2.3.9版本,各种Mock之后,我们决定进行第一轮SpringBoot2.3.9版本基础库的检验,检验我们的SpringBoot2.3.9基础库是否有无问题,现实是残酷的,我们升级了SpringBoot2.3.9版本的服务经过Zuul网关发现全部乱码了,不经过网关就一切正常,所以本节我们就分析下乱码的原因以及解决的方法。


我们的基础框架中在基于SpringBoot2.0.4和SpringBoot2.3.9版本中都是使用的JackSon作为数据序列化和反序列化框架,我们在适配SpringBoot2.3.9版本时候,也没有对JackSon做其他扩展改动,但是为什么就乱码了呢?所以围绕这个问题,既然乱码,那肯定就是编码格式问题,围绕这个问题点,我决定对比下SpringBoot2.0.4和SpringBoot2.3.9版本的JackSon相关设置,入口配置类HttpMessageConvertersAutoConfiguration,其中会设置HttpMessageConverters


SpringBoot2.0.4

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
    ......
    protected final List<HttpMessageConverter<?>> getMessageConverters() {
        if (this.messageConverters == null) {
            this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
            configureMessageConverters(this.messageConverters);
            if (this.messageConverters.isEmpty()) {
                addDefaultHttpMessageConverters(this.messageConverters);
            }
            extendMessageConverters(this.messageConverters);
        }
        return this.messageConverters;
    }
    ......
    protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        ......
        if (jackson2Present) {
            ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build();
            messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper));
        }
        ......
    }
}

public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    ......
    protected void init(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
        setDefaultCharset(DEFAULT_CHARSET);
        DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
        prettyPrinter.indentObjectsWith(new DefaultIndenter("  ", "\ndata:"));
        this.ssePrettyPrinter = prettyPrinter;
    }
    ......
}

public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
    protected void addDefaultHeaders(HttpHeaders headers, T t, MediaType contentType) throws IOException{
        if (headers.getContentType() == null) {
            MediaType contentTypeToUse = contentType;
            ......
            if (contentTypeToUse != null) {
                if (contentTypeToUse.getCharset() == null) {
                    Charset defaultCharset = getDefaultCharset();
                    if (defaultCharset != null) {
                        contentTypeToUse = new MediaType(contentTypeToUse, defaultCharset);
                    }
                }
                headers.setContentType(contentTypeToUse);
            }
        }
        ......
    }
}

在SpringBoot2.0.4版本中,我们可以看出在AbstractJackson2HttpMessageConverter这个初始化过程中会设置默认的编码格式UTF-8,所以我们在addDefaultHeaders这个方法中,最终设置的headers.setContentType(contentTypeToUse);这个格式会是MediaType#APPLICATION_JSON_UTF8 => application/json;charset=UTF-8


SpringBoot2.3.9

在SpringBoot2.3.9版本中,WebMvcConfigurationSupport、AbstractHttpMessageConverter变化都不大,基本都是一样的逻辑,唯一有改动的地方为AbstractJackson2HttpMessageConverter这个类,所以我们重点看这个

public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
    /**
     * The default charset used by the converter.
     */
    @Nullable
    @Deprecated
    public static final Charset DEFAULT_CHARSET = null;
    ......
    protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
        DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
        prettyPrinter.indentObjectsWith(new DefaultIndenter("  ", "\ndata:"));
        this.ssePrettyPrinter = prettyPrinter;
    }
    ......
}

我们看见初始化AbstractJackson2HttpMessageConverter这个类中直接删掉了设置默认编码格式,官方给的解释大致为:认为这个默认编码已经不需要设置了,因为现在绝大多数的浏览器或者框架都默认会设置编码格式!,所以我们在回头看看如果没有这个默认编码格式,那么最终的headers.setContentType(contentTypeToUse);这个会变成什么呢?变成MediaType#APPLICATION_JSON => application/json 这样的格式会发生什么呢?我们看看经过网关Zuul会有什么现象!


网关Zuul分析

public class DispatcherServlet extends FrameworkServlet {
    ......
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
      ......
      //这个就会调用Zuul的run()
      mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      ......
    }
    ......
}

Zuul的route阶段会请求下游服务获取数据,经过Debug发现在Zuul=>route阶段请求下游服务获取出来的数据编码格式均为utf-8,然后Zuul=>post数据包装阶段数据也为正常的utf-8,route、post阶段获取出来的数据都没有乱码,那么说明Zuul的请求和包装阶段是正常的,继续跟踪后发现DispatcherServlet#doDispatch最终返回的Response数据中characterEncoding格式为ISO-8859-1的格式,此时我们最终的Response数据如果有中文,那么就会变成一堆❓❓❓这样的问号。Zuul相关原理可参阅Spring Cloud Zuul 分析(三)之ZuulFilter调用过程

解决方式

#方式一
server:
  servlet:
    encoding:
      charset: utf-8
      #只设置Response
      #HttpEncodingAutoConfiguration#CharacterEncodingFilter中会使用forceResponse
      #CharacterEncodingFilter#doFilterInternal中会设置HttpServletResponse.setCharacterEncoding
      forceResponse: true
      enabled: true

#方式二
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
  @Override
  protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    for (HttpMessageConverter<?> converter : converters) {
      if (converter instanceof MappingJackson2HttpMessageConverter) {
        ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);
     }
    }
  }
}

针对SpringBoot2.3.9 => Spring-Web5.2版本的乱码问题,这里也做了产生的原因和修改的方式,这里笔者使用的方式一,因为方式二是针对的具体某一种HttpMessageConverter类型做的设置默认编码格式,具体使用哪一种方式根据各自业务情况决定!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容