Spring Cloud Gateway 重寫response中文亂碼
一劍寒霜十四州
發(fā)布于 2021-04-02 · 4.3w瀏覽 8贊

一、問題定位

在做微服務(wù)項目的時候,使用Spring Cloud Gateway會時不時亂碼

亂碼字符是這樣的:“???”

在排除了一系列數(shù)據(jù)庫編碼設(shè)置、resoonse流字符設(shè)置等可能導(dǎo)致亂碼的情況后,直接調(diào)用服務(wù)不亂碼,通過網(wǎng)關(guān)就會亂碼,確定了是響應(yīng)數(shù)據(jù)在gateway中發(fā)生了亂碼。


二、錯誤使用

ServerHttpResponseDecorator decorator = new ServerHttpResponseDecorator(response) {            @Override            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {                if (body instanceof Flux) {                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>)body;                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {                        List<String> list = Lists.newArrayList();                        // gateway 針對返回參數(shù)過長的情況下會分段返回,使用如下方式接受返回參數(shù)則可避免                        dataBuffers.forEach(dataBuffer -> {                            // probably should reuse buffers                            byte[] content = new byte[dataBuffer.readableByteCount()];                            dataBuffer.read(content);                            // 釋放掉內(nèi)存                            DataBufferUtils.release(dataBuffer);                            list.add(new String(content, StandardCharsets.UTF_8));                        });                        // 將多次返回的參數(shù)拼接起來                        String responseData = joiner.join(list);                        // 重置返回參數(shù)                        String result = response(responseData);                        byte[] uppedContent =                                new String(result.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8).getBytes();                        // 修改后的返回參數(shù)應(yīng)該重置長度,否則如果修改后的參數(shù)長度超出原始參數(shù)長度時會導(dǎo)致客戶端接收到的參數(shù)丟失一部分                        response.getHeaders().setContentLength(uppedContent.length);                        return bufferFactory.wrap(uppedContent);                    }));                }                return super.writeWith(body);            }        };

網(wǎng)關(guān)是請求的出入口如果響應(yīng)過大的話,F(xiàn)lux會進行截斷,可能會導(dǎo)致亂碼。。按照我的理解是,響應(yīng)數(shù)據(jù)在到達Flux時,已經(jīng)是一串byte字節(jié)數(shù)據(jù),如果這個byte數(shù)據(jù)被截斷,大概率其截斷點不會剛好是某一個字符的最后一個字節(jié)數(shù)據(jù)。比如一個字符串中,每個字符占10個字節(jié),只有當截取的字節(jié)長度為10、20或30時,也就是截取下來字符剛好都是完整的字符才不會出現(xiàn)亂碼。但是當截取的字節(jié)長度為12、23或34時,剛好只截取到了最后一個字符的一部分字節(jié)數(shù)據(jù),而另一部分丟失了時就會出現(xiàn)亂碼。


三、正確使用

ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {

   @Override

   public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

       if (body instanceof Flux) {

           Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;

           return super.writeWith(flux.buffer().map(dataBuffers -> {

               DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();

               //dataBuffer合并成一個,解決獲取結(jié)果不全問題

               DataBuffer join = dataBufferFactory.join(dataBuffers);

               byte[] content = new byte[join.readableByteCount()];

               join.read(content);

               //釋放掉內(nèi)存

               DataBufferUtils.release(join);

               //formatResponse對獲取的結(jié)果進行修改,可根據(jù)自身業(yè)務(wù)需求做修改

               String response = formatResponse(content, exchange);

               //修改響應(yīng)結(jié)果長度

               byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8);

               originalResponse.getHeaders().setContentLength(responseBytes.length);

               return bufferFactory.wrap(responseBytes);

           }));

       }

       return super.writeWith(body);

   }

};

結(jié)論:DataBufferFactory的join方法能解決獲取結(jié)果不全問題,使用它拼接的流是完整沒有截斷的


一劍寒霜十四州
小怪獸,我是明非啊!
瀏覽 4.3w
8
相關(guān)推薦
最新評論
贊過的人 8
評論加載中...

暫無評論,快來評論吧!