Use UTF-8 with JSON in MockHttpServletResponse

This commit makes MockHttpServletResponse consistent with the other
parts of the framework where the body of a response is read as an UTF-8
String when the content type is application/json or similar, overriding
the ISO-8859-1 default Servlet encoding.

Closes gh-33019
This commit is contained in:
Sébastien Deleuze 2024-06-13 16:02:28 +02:00
parent 2116d71d81
commit 1d363e5a41
5 changed files with 21 additions and 10 deletions

View File

@ -24,6 +24,7 @@ import java.io.PrintWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -72,6 +73,8 @@ public class MockHttpServletResponse implements HttpServletResponse {
private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
private static final MediaType APPLICATION_PLUS_JSON = new MediaType("application", "*+json");
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// ServletResponse properties // ServletResponse properties
@ -348,6 +351,10 @@ public class MockHttpServletResponse implements HttpServletResponse {
if (mediaType.getCharset() != null) { if (mediaType.getCharset() != null) {
setExplicitCharacterEncoding(mediaType.getCharset().name()); setExplicitCharacterEncoding(mediaType.getCharset().name());
} }
else if (mediaType.isCompatibleWith(MediaType.APPLICATION_JSON) ||
mediaType.isCompatibleWith(APPLICATION_PLUS_JSON)) {
this.characterEncoding = StandardCharsets.UTF_8.name();
}
} }
catch (Exception ex) { catch (Exception ex) {
// Try to get charset value anyway // Try to get charset value anyway

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -257,7 +257,7 @@ public class ContentResultMatchers {
*/ */
public ResultMatcher json(String jsonContent, JsonComparator comparator) { public ResultMatcher json(String jsonContent, JsonComparator comparator) {
return result -> { return result -> {
String content = result.getResponse().getContentAsString(StandardCharsets.UTF_8); String content = result.getResponse().getContentAsString();
comparator.assertIsMatch(jsonContent, content); comparator.assertIsMatch(jsonContent, content);
}; };
} }

View File

@ -17,7 +17,6 @@
package org.springframework.test.web.servlet.result; package org.springframework.test.web.servlet.result;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.JsonPath;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
@ -238,7 +237,7 @@ public class JsonPathResultMatchers {
} }
private String getContent(MvcResult result) throws UnsupportedEncodingException { private String getContent(MvcResult result) throws UnsupportedEncodingException {
String content = result.getResponse().getContentAsString(StandardCharsets.UTF_8); String content = result.getResponse().getContentAsString();
if (StringUtils.hasLength(this.prefix)) { if (StringUtils.hasLength(this.prefix)) {
try { try {
String reason = String.format("Expected a JSON payload prefixed with \"%s\" but found: %s", String reason = String.format("Expected a JSON payload prefixed with \"%s\" but found: %s",

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
package org.springframework.test.web.servlet.result; package org.springframework.test.web.servlet.result;
import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Map; import java.util.Map;
@ -28,7 +27,6 @@ import jakarta.servlet.http.HttpSession;
import org.springframework.core.style.ToStringCreator; import org.springframework.core.style.ToStringCreator;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
@ -250,9 +248,7 @@ public class PrintingResultHandler implements ResultHandler {
this.printer.printValue("Error message", response.getErrorMessage()); this.printer.printValue("Error message", response.getErrorMessage());
this.printer.printValue("Headers", getResponseHeaders(response)); this.printer.printValue("Headers", getResponseHeaders(response));
this.printer.printValue("Content type", response.getContentType()); this.printer.printValue("Content type", response.getContentType());
String body = (MediaType.APPLICATION_JSON_VALUE.equals(response.getContentType()) ? this.printer.printValue("Body", response.getContentAsString());
response.getContentAsString(StandardCharsets.UTF_8) : response.getContentAsString());
this.printer.printValue("Body", body);
this.printer.printValue("Forwarded URL", response.getForwardedUrl()); this.printer.printValue("Forwarded URL", response.getForwardedUrl());
this.printer.printValue("Redirected URL", response.getRedirectedUrl()); this.printer.printValue("Redirected URL", response.getRedirectedUrl());
printCookies(response.getCookies()); printCookies(response.getCookies());

View File

@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.http.MediaType;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -620,4 +621,12 @@ class MockHttpServletResponseTests {
assertThat(contentTypeHeader).isEqualTo("text/plain"); assertThat(contentTypeHeader).isEqualTo("text/plain");
} }
@Test // gh-33019
void contentAsStringEncodingWithJson() throws IOException {
String content = "{\"name\": \"Jürgen\"}";
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(content);
assertThat(response.getContentAsString()).isEqualTo(content);
}
} }