Deprecate MediaType.APPLICATION_JSON_UTF8

This commit deprecates MediaType.APPLICATION_JSON_UTF8 and
MediaType.APPLICATION_PROBLEM_JSON_UTF8 in favor of
MediaType.APPLICATION_JSON and MediaType.APPLICATION_PROBLEM_JSON since
UTF-8 encoding is now handled correctly by most browsers
(related bug has been fixed in Chrome since September 2017).

MediaType.APPLICATION_JSON is now used as the default JSON content type.

Closes gh-22788
This commit is contained in:
Sebastien Deleuze
2019-04-30 16:05:09 +02:00
parent 2e6059f6b0
commit 89454e69c3
39 changed files with 144 additions and 147 deletions

View File

@@ -48,6 +48,7 @@ import org.springframework.util.StringUtils;
* @see <a href="https://tools.ietf.org/html/rfc7231#section-3.1.1.1">
* HTTP 1.1: Semantics and Content, section 3.1.1.1</a>
*/
@SuppressWarnings("deprecation")
public class MediaType extends MimeType implements Serializable {
private static final long serialVersionUID = 2069937152339670231L;
@@ -84,7 +85,6 @@ public class MediaType extends MimeType implements Serializable {
/**
* Public constant media type for {@code application/json}.
* @see #APPLICATION_JSON_UTF8
*/
public static final MediaType APPLICATION_JSON;
@@ -96,24 +96,24 @@ public class MediaType extends MimeType implements Serializable {
/**
* Public constant media type for {@code application/json;charset=UTF-8}.
*
* <p>This {@link MediaType#APPLICATION_JSON} variant should be used to set JSON
* content type because while
* <a href="https://tools.ietf.org/html/rfc7159#section-11">RFC7159</a>
* clearly states that "no charset parameter is defined for this registration", some
* browsers require it for interpreting correctly UTF-8 special characters.
* @deprecated Deprecated as of Spring Framework 5.2 in favor of {@link #APPLICATION_JSON}
* since major browsers like Chrome
* <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=438464">
* now comply with the specification</a> and interpret correctly UTF-8 special
* characters without requiring a {@code charset=UTF-8} parameter.
*/
@Deprecated
public static final MediaType APPLICATION_JSON_UTF8;
/**
* A String equivalent of {@link MediaType#APPLICATION_JSON_UTF8}.
*
* <p>This {@link MediaType#APPLICATION_JSON_VALUE} variant should be used to set JSON
* content type because while
* <a href="https://tools.ietf.org/html/rfc7159#section-11">RFC7159</a>
* clearly states that "no charset parameter is defined for this registration", some
* browsers require it for interpreting correctly UTF-8 special characters.
* @deprecated Deprecated as of Spring Framework 5.2 in favor of {@link #APPLICATION_JSON_VALUE}
* since major browsers like Chrome
* <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=438464">
* now comply with the specification</a> and interpret correctly UTF-8 special
* characters without requiring a {@code charset=UTF-8} parameter.
*/
@Deprecated
public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
/**
@@ -157,13 +157,25 @@ public class MediaType extends MimeType implements Serializable {
* @since 5.0
* @see <a href="https://tools.ietf.org/html/rfc7807#section-6.1">
* Problem Details for HTTP APIs, 6.1. application/problem+json</a>
* @deprecated Deprecated as of Spring Framework 5.2 in favor of {@link #APPLICATION_PROBLEM_JSON}
* since major browsers like Chrome
* <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=438464">
* now comply with the specification</a> and interpret correctly UTF-8 special
* characters without requiring a {@code charset=UTF-8} parameter.
*/
@Deprecated
public static final MediaType APPLICATION_PROBLEM_JSON_UTF8;
/**
* A String equivalent of {@link MediaType#APPLICATION_PROBLEM_JSON_UTF8}.
* @since 5.0
* @deprecated Deprecated as of Spring Framework 5.2 in favor of {@link #APPLICATION_PROBLEM_JSON_VALUE}
* since major browsers like Chrome
* <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=438464">
* now comply with the specification</a> and interpret correctly UTF-8 special
* characters without requiring a {@code charset=UTF-8} parameter.
*/
@Deprecated
public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = "application/problem+json;charset=UTF-8";
/**

View File

@@ -18,7 +18,6 @@ package org.springframework.http.codec.json;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -61,8 +60,8 @@ public abstract class Jackson2CodecSupport {
private static final List<MimeType> DEFAULT_MIME_TYPES = Collections.unmodifiableList(
Arrays.asList(
new MimeType("application", "json", StandardCharsets.UTF_8),
new MimeType("application", "*+json", StandardCharsets.UTF_8)));
new MimeType("application", "json"),
new MimeType("application", "*+json")));
protected final Log logger = HttpLogging.forLogName(getClass());

View File

@@ -16,8 +16,6 @@
package org.springframework.http.codec.json;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
@@ -37,8 +35,8 @@ import org.springframework.util.MimeType;
public class Jackson2SmileDecoder extends AbstractJackson2Decoder {
private static final MimeType[] DEFAULT_SMILE_MIME_TYPES = new MimeType[] {
new MimeType("application", "x-jackson-smile", StandardCharsets.UTF_8),
new MimeType("application", "*+x-jackson-smile", StandardCharsets.UTF_8)};
new MimeType("application", "x-jackson-smile"),
new MimeType("application", "*+x-jackson-smile")};
public Jackson2SmileDecoder() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.springframework.http.codec.json;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
@@ -41,8 +40,8 @@ import org.springframework.util.MimeType;
public class Jackson2SmileEncoder extends AbstractJackson2Encoder {
private static final MimeType[] DEFAULT_SMILE_MIME_TYPES = new MimeType[] {
new MimeType("application", "x-jackson-smile", StandardCharsets.UTF_8),
new MimeType("application", "*+x-jackson-smile", StandardCharsets.UTF_8)};
new MimeType("application", "x-jackson-smile"),
new MimeType("application", "*+x-jackson-smile")};
public Jackson2SmileEncoder() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ package org.springframework.http.converter.json;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicReference;
@@ -49,6 +48,7 @@ import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.TypeUtils;
@@ -72,7 +72,9 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
/**
* The default charset used by the converter.
*/
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
@Nullable
@Deprecated
public static final Charset DEFAULT_CHARSET = null;
protected ObjectMapper objectMapper;
@@ -86,7 +88,6 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
setDefaultCharset(DEFAULT_CHARSET);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
this.ssePrettyPrinter = prettyPrinter;
@@ -250,6 +251,9 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
if (getDefaultCharset() == null && outputMessage instanceof ServletServerHttpResponse) {
((ServletServerHttpResponse)outputMessage).getServletResponse().setCharacterEncoding("UTF-8");
}
MediaType contentType = outputMessage.getHeaders().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.http.converter.xml;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
@@ -57,9 +59,9 @@ public class MappingJackson2XmlHttpMessageConverter extends AbstractJackson2Http
* @see Jackson2ObjectMapperBuilder#xml()
*/
public MappingJackson2XmlHttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, new MediaType("application", "xml"),
new MediaType("text", "xml"),
new MediaType("application", "*+xml"));
super(objectMapper, new MediaType("application", "xml", StandardCharsets.UTF_8),
new MediaType("text", "xml", StandardCharsets.UTF_8),
new MediaType("application", "*+xml", StandardCharsets.UTF_8));
Assert.isInstanceOf(XmlMapper.class, objectMapper, "XmlMapper required");
}

View File

@@ -72,7 +72,6 @@ public class Jackson2JsonDecoderTests extends AbstractDecoderTestCase<Jackson2Js
@Test
public void canDecode() {
assertTrue(decoder.canDecode(forClass(Pojo.class), APPLICATION_JSON));
assertTrue(decoder.canDecode(forClass(Pojo.class), APPLICATION_JSON_UTF8));
assertTrue(decoder.canDecode(forClass(Pojo.class), APPLICATION_STREAM_JSON));
assertTrue(decoder.canDecode(forClass(Pojo.class), null));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,7 +44,6 @@ import org.springframework.util.MimeTypeUtils;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.*;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
import static org.springframework.http.MediaType.APPLICATION_STREAM_JSON;
import static org.springframework.http.MediaType.APPLICATION_XML;
@@ -67,7 +66,6 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTestCase<Jackson2Js
public void canEncode() {
ResolvableType pojoType = ResolvableType.forClass(Pojo.class);
assertTrue(this.encoder.canEncode(pojoType, APPLICATION_JSON));
assertTrue(this.encoder.canEncode(pojoType, APPLICATION_JSON_UTF8));
assertTrue(this.encoder.canEncode(pojoType, APPLICATION_STREAM_JSON));
assertTrue(this.encoder.canEncode(pojoType, null));

View File

@@ -102,7 +102,7 @@ public class MultipartHttpMessageWriterTests extends AbstractLeakCheckingTestCas
bodyBuilder.part("name 2", "value 2+2");
bodyBuilder.part("logo", logo);
bodyBuilder.part("utf8", utf8);
bodyBuilder.part("json", new Foo("bar"), MediaType.APPLICATION_JSON_UTF8);
bodyBuilder.part("json", new Foo("bar"), MediaType.APPLICATION_JSON);
bodyBuilder.asyncPart("publisher", publisher, String.class);
Mono<MultiValueMap<String, HttpEntity<?>>> result = Mono.just(bodyBuilder.build());
@@ -144,7 +144,7 @@ public class MultipartHttpMessageWriterTests extends AbstractLeakCheckingTestCas
part = requestParts.getFirst("json");
assertEquals("json", part.name());
assertEquals(MediaType.APPLICATION_JSON_UTF8, part.headers().getContentType());
assertEquals(MediaType.APPLICATION_JSON, part.headers().getContentType());
String value = StringDecoder.textPlainOnly(false).decodeToMono(part.content(),
ResolvableType.forClass(String.class), MediaType.TEXT_PLAIN,

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -139,7 +139,7 @@ public class MappingJackson2HttpMessageConverterTests {
assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]"));
assertTrue(result.contains("\"bool\":true"));
assertTrue(result.contains("\"bytes\":\"AQI=\""));
assertEquals("Invalid content-type", new MediaType("application", "json", StandardCharsets.UTF_8),
assertEquals("Invalid content-type", MediaType.APPLICATION_JSON,
outputMessage.getHeaders().getContentType());
}
@@ -161,7 +161,7 @@ public class MappingJackson2HttpMessageConverterTests {
assertTrue(result.contains("\"array\":[\"Foo\",\"Bar\"]"));
assertTrue(result.contains("\"bool\":true"));
assertTrue(result.contains("\"bytes\":\"AQI=\""));
assertEquals("Invalid content-type", new MediaType("application", "json", StandardCharsets.UTF_8),
assertEquals("Invalid content-type", MediaType.APPLICATION_JSON,
outputMessage.getHeaders().getContentType());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -143,7 +143,7 @@ public class ProtobufHttpMessageConverterTests {
new ProtobufHttpMessageConverter.ProtobufJavaUtilSupport(null, null),
this.extensionRegistry);
MockHttpOutputMessage outputMessage = new MockHttpOutputMessage();
MediaType contentType = MediaType.APPLICATION_JSON_UTF8;
MediaType contentType = MediaType.APPLICATION_JSON;
this.converter.write(this.testMsg, contentType, outputMessage);
assertEquals(contentType, outputMessage.getHeaders().getContentType());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
package org.springframework.http.converter.smile;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
@@ -90,7 +89,7 @@ public class MappingJackson2SmileHttpMessageConverterTests {
body.setBytes(new byte[]{0x1, 0x2});
converter.write(body, null, outputMessage);
assertArrayEquals(mapper.writeValueAsBytes(body), outputMessage.getBodyAsBytes());
assertEquals("Invalid content-type", new MediaType("application", "x-jackson-smile", StandardCharsets.UTF_8),
assertEquals("Invalid content-type", new MediaType("application", "x-jackson-smile"),
outputMessage.getHeaders().getContentType());
}