基于springboot2.2.2源码对响应头编码剖析

1.问题描述:

    produces = "application/json;charset=GBK"不生效,查询了资料了发现没有文章对此说明,追踪源码发现对应的问题,所以记录起来避免其他人也采坑

2.主流工具类的实现,例如fastjson、jsackjson、GsonJson

3.工具类剖析,重要!!!

  关键信息:http重要接口类HttpMessageConverter

  JackJson:

  关键重要类:AbstractJackson2HttpMessageConverter

  AbstractJackson2HttpMessageConverter-->AbstractGenericHttpMessageConverter--> AbstractHttpMessageConverter-->HttpMessageConverter 

protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        MediaType contentType = outputMessage.getHeaders().getContentType();
        JsonEncoding encoding = getJsonEncoding(contentType);
        JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
        try {
            writePrefix(generator, object);

            Object value = object;
            Class<?> serializationView = null;
            FilterProvider filters = null;
            JavaType javaType = null;

            if (object instanceof MappingJacksonValue) {
                MappingJacksonValue container = (MappingJacksonValue) object;
                value = container.getValue();
                serializationView = container.getSerializationView();
                filters = container.getFilters();
            }
            if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
                javaType = getJavaType(type, null);
            }

            ObjectWriter objectWriter = (serializationView != null ?
                    this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
            if (filters != null) {
                objectWriter = objectWriter.with(filters);
            }
            if (javaType != null && javaType.isContainerType()) {
                objectWriter = objectWriter.forType(javaType);
            }
            SerializationConfig config = objectWriter.getConfig();
            if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
                    config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
                objectWriter = objectWriter.with(this.ssePrettyPrinter);
            }
            objectWriter.writeValue(generator, value);

            writeSuffix(generator, object);
            generator.flush();
        }
        catch (InvalidDefinitionException ex) {
            throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
        }
        catch (JsonProcessingException ex) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
        }
    }

关键点:JsonEncoding

protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) {
        if (contentType != null && contentType.getCharset() != null) {
            Charset charset = contentType.getCharset();
            for (JsonEncoding encoding : JsonEncoding.values()) {
                if (charset.name().equals(encoding.getJavaName())) {
                    return encoding;
                }
            }
        }
        return JsonEncoding.UTF8;
    }
关键点:没有对GBK编码的支持

public enum JsonEncoding {
    UTF8("UTF-8", false, 8), // N/A for big-endian, really
        UTF16_BE("UTF-16BE", true, 16),
        UTF16_LE("UTF-16LE", false, 16),
        UTF32_BE("UTF-32BE", true, 32),
        UTF32_LE("UTF-32LE", false, 32)
        ;
    
    private final String _javaName;

    private final boolean _bigEndian;

    private final int _bits;
    
    JsonEncoding(String javaName, boolean bigEndian, int bits)
    {
        _javaName = javaName;
        _bigEndian = bigEndian;
        _bits = bits;
    }

    /**
     * Method for accessing encoding name that JDK will support.
     *
     * @return Matching encoding name that JDK will support.
     */
    public String getJavaName() { return _javaName; }

    /**
     * Whether encoding is big-endian (if encoding supports such
     * notion). If no such distinction is made (as is the case for
     * {@link #UTF8}), return value is undefined.
     *
     * @return True for big-endian encodings; false for little-endian
     *   (or if not applicable)
     */
    public boolean isBigEndian() { return _bigEndian; }

    public int bits() { return _bits; }
}

FastJson

关键重要类:FastJsonHttpMessageConverter

FastJsonHttpMessageConverter--> AbstractHttpMessageConverter-->HttpMessageConverter

关键点: fastJsonConfig.getCharset(),取的是默认的字符编码,这个初始化可以设置

protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

        ByteArrayOutputStream outnew = new ByteArrayOutputStream();
        try {
            HttpHeaders headers = outputMessage.getHeaders();
            SerializeFilter[] globalFilters = fastJsonConfig.getSerializeFilters();
            List<SerializeFilter> allFilters = new ArrayList<SerializeFilter>(Arrays.asList(globalFilters));

            boolean isJsonp = false;
            Object value = strangeCodeForJackson(object);

            if (value instanceof FastJsonContainer) {
                FastJsonContainer fastJsonContainer = (FastJsonContainer) value;
                PropertyPreFilters filters = fastJsonContainer.getFilters();
                allFilters.addAll(filters.getFilters());
                value = fastJsonContainer.getValue();
            }
       
            if (value instanceof MappingFastJsonValue) {
                if (!StringUtils.isEmpty(((MappingFastJsonValue) value).getJsonpFunction())) {
                    isJsonp = true;
                }
            } else if (value instanceof JSONPObject) {
                isJsonp = true;
            }


            int len = JSON.writeJSONString(outnew, //
                    fastJsonConfig.getCharset(), //
                    value, //
                    fastJsonConfig.getSerializeConfig(), //
                    //fastJsonConfig.getSerializeFilters(), //
                    allFilters.toArray(new SerializeFilter[allFilters.size()]),
                    fastJsonConfig.getDateFormat(), //
                    JSON.DEFAULT_GENERATE_FEATURE, //
                    fastJsonConfig.getSerializerFeatures());

            if (isJsonp) {
                headers.setContentType(APPLICATION_JAVASCRIPT);
            }

            if (fastJsonConfig.isWriteContentLength()) {
                headers.setContentLength(len);
            }

            outnew.writeTo(outputMessage.getBody());

        } catch (JSONException ex) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
        } finally {
            outnew.close();
        }
    }
 

GSON

springboot对Gson的支持也比较简单

<dependency>

  <groupId>com.google.code.gson</groupId>

  <artifactId>gson</artifactId>

</dependency>

<dependency>

  <groupId>org.springframework.boot</groupId>

  <artifactId>spring-boot-actuator</artifactId>

  <exclusions>

    <exclusion>

    <groupId>com.fasterxml.jackson.datatype</groupId>

    <artifactId>jackson-datatype-jsr310</artifactId>

</exclusion>

 

</exclusions>

</dependency>

关键重要类:AbstractJsonHttpMessageConverter

AbstractJsonHttpMessageConverter-->AbstractGenericHttpMessageConverter--> AbstractHttpMessageConverter-->HttpMessageConverter

关键点:AbstractJsonHttpMessageConverter-->getWriter

protected final void writeInternal(Object o, @Nullable Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        Writer writer = getWriter(outputMessage);
        if (this.jsonPrefix != null) {
            writer.append(this.jsonPrefix);
        }
        try {
            writeInternal(o, type, writer);
        }
        catch (Exception ex) {
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
        }
        writer.flush();
    }

private static Writer getWriter(HttpOutputMessage outputMessage) throws IOException {
        return new OutputStreamWriter(outputMessage.getBody(), getCharset(outputMessage.getHeaders()));
   }

 

4.总结:

可以明显发现jackjson是没有对GBK编码的支持,fastjson初始化设置字符集编码外(但是设置了默认编码后,整个项目就是用了这个字符集编码(示例如下@Bean),如果项目有需要对GBK的支持建议使用respone方法去拓展或者引用gson)

 

@Bean

  public HttpMessageConverters fastJsonConfigure(){

     FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();

     FastJsonConfig fastJsonConfig = new FastJsonConfig();

    fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);

    //日期格式化

    fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

    fastJsonConfig.setCharset(Charset.forName("GBK"));

    converter.setFastJsonConfig(fastJsonConfig);

    return new HttpMessageConverters(converter);

  }

采用respone方法也是最简单的方法

response.setContentType("application/json;charset=GBK");

response.getWriter().write(JSONObject.toJSONString(object));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值