From 9b30d46ff4653c411ee4c8edb89b9bafa11b2ee9 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 10 Dec 2019 16:37:31 +0000 Subject: [PATCH] JSON charset handling in StringHttpMessageConverter This commit restores the interpretation of JSON as UTF-8 by default that was removed in #bc205e0 and also ensures a charset is not appended automatically to "application/json". Closes gh-24123 --- .../converter/StringHttpMessageConverter.java | 16 ++++++++++++++ .../StringHttpMessageConverterTests.java | 22 +++++++++++++++++++ ...nnotationControllerHandlerMethodTests.java | 4 ++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java index d1365943a5a8..7abccf5a06fd 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/StringHttpMessageConverter.java @@ -100,6 +100,18 @@ protected Long getContentLength(String str, @Nullable MediaType contentType) { return (long) str.getBytes(charset).length; } + + @Override + protected void addDefaultHeaders(HttpHeaders headers, String s, @Nullable MediaType mediaType) throws IOException { + if (headers.getContentType() == null ) { + if (mediaType != null && mediaType.isCompatibleWith(MediaType.APPLICATION_JSON)) { + // Prevent charset parameter for JSON.. + headers.setContentType(mediaType); + } + } + super.addDefaultHeaders(headers, s, mediaType); + } + @Override protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException { HttpHeaders headers = outputMessage.getHeaders(); @@ -130,6 +142,10 @@ private Charset getContentTypeCharset(@Nullable MediaType contentType) { if (contentType != null && contentType.getCharset() != null) { return contentType.getCharset(); } + else if (contentType != null && contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) { + // Matching to AbstractJackson2HttpMessageConverter#DEFAULT_CHARSET + return StandardCharsets.UTF_8; + } else { Charset charset = getDefaultCharset(); Assert.state(charset != null, "No default charset"); diff --git a/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java index 8c66bc4de965..c0e8792d33f6 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/StringHttpMessageConverterTests.java @@ -70,6 +70,16 @@ public void read() throws IOException { assertThat(result).as("Invalid result").isEqualTo(body); } + @Test // gh-24123 + public void readJson() throws IOException { + String body = "{\"result\":\"\u0414\u0410\"}"; + MockHttpInputMessage inputMessage = new MockHttpInputMessage(body.getBytes(StandardCharsets.UTF_8)); + inputMessage.getHeaders().setContentType(MediaType.APPLICATION_JSON); + String result = this.converter.read(String.class, inputMessage); + + assertThat(result).as("Invalid result").isEqualTo(body); + } + @Test public void writeDefaultCharset() throws IOException { String body = "H\u00e9llo W\u00f6rld"; @@ -82,6 +92,18 @@ public void writeDefaultCharset() throws IOException { assertThat(headers.getAcceptCharset().isEmpty()).isTrue(); } + @Test // gh-24123 + public void writeJson() throws IOException { + String body = "{\"foo\":\"bar\"}"; + this.converter.write(body, MediaType.APPLICATION_JSON, this.outputMessage); + + HttpHeaders headers = this.outputMessage.getHeaders(); + assertThat(this.outputMessage.getBodyAsString(StandardCharsets.UTF_8)).isEqualTo(body); + assertThat(headers.getContentType()).isEqualTo(MediaType.APPLICATION_JSON); + assertThat(headers.getContentLength()).isEqualTo(body.getBytes(StandardCharsets.UTF_8).length); + assertThat(headers.getAcceptCharset().isEmpty()).isTrue(); + } + @Test public void writeUTF8() throws IOException { String body = "H\u00e9llo W\u00f6rld"; diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index 502b11ab5c8f..565f785d296f 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -1011,7 +1011,7 @@ public void overlappingMessageConvertersRequestBody() throws Exception { request.addHeader("Accept", "application/json, text/javascript, */*"); MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); - assertThat(response.getHeader("Content-Type")).as("Invalid content-type").isEqualTo("application/json;charset=ISO-8859-1"); + assertThat(response.getHeader("Content-Type")).as("Invalid content-type").isEqualTo("application/json"); } @Test @@ -1548,7 +1548,7 @@ public void testHeadersCondition() throws Exception { getServlet().service(request, response); assertThat(response.getStatus()).isEqualTo(200); - assertThat(response.getHeader("Content-Type")).isEqualTo("application/json;charset=ISO-8859-1"); + assertThat(response.getHeader("Content-Type")).isEqualTo("application/json"); assertThat(response.getContentAsString()).isEqualTo("homeJson"); }