Overview
27214 introduced support for setting the default character encoding in MockHttpServletResponse
, and the focus of this issue is to make that configurable when building an instance of MockMvc
.
Deliverables
- [x] Introduce
defaultResponseCharacterEncoding(Charset)
inConfigurableMockMvcBuilder
and use it to configure the default character encoding in the underlyingMockHttpServletResponse
.
Comment From: lonre
Hi @sbrannen
Could you please help to check this test case, it still fails
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
import org.junit.jupiter.api.Test;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
class ResponseBodyTests {
@Test
void text() throws Exception {
standaloneSetup(new PersonController()).defaultResponseCharacterEncoding(UTF_8).build()
.perform(post("/person").characterEncoding(UTF_8).content("Jürgen"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string("Jürgen"));
}
@RestController
private static class PersonController {
@PostMapping("/person")
String post(@RequestBody String content) {
return content;
}
}
}
Comment From: sbrannen
Hi @lonre,
Thanks for providing the failing test case.
I took a look at it, and it appears that the defaultResponseCharacterEncoding
has no effect for String
payloads with the default configuration for Spring MVC.
Behind the scenes, a default instance of StringHttpMessageConverter
is used for reading and writing the content, and in order to have it treat the content as UTF-8 encoded text for both the request and the response, you have to explicitly specify that as follows.
class RestUtf8StringPayloadTests {
@Test
void test() throws Exception {
MockMvc mockMvc = standaloneSetup(new PersonController())
// .defaultResponseCharacterEncoding(StandardCharsets.UTF_8)
.build();
mockMvc.perform(post("/person")
.contentType("text/plain;charset=UTF-8")
.accept("text/plain;charset=UTF-8")
.content("Jürgen")
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().encoding(StandardCharsets.UTF_8))
.andExpect(content().string("Jürgen"));
}
@RestController
private static class PersonController {
@PostMapping("/person")
String post(@RequestBody String content) {
return content;
}
}
}
As an alternative to specifying .accept("text/plain;charset=UTF-8")
for the POST with MockMvc
, you can specify that the controller method produces text/plain;charset=UTF-8
as in the following modified example.
class RestUtf8StringPayloadTests {
@Test
void test() throws Exception {
standaloneSetup(new PersonController()).build()
.perform(post("/person")
.contentType("text/plain;charset=UTF-8")
.content("Jürgen")
)
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().encoding(StandardCharsets.UTF_8))
.andExpect(content().string("Jürgen"));
}
@RestController
private static class PersonController {
@PostMapping(path = "/person", produces = "text/plain;charset=UTF-8")
String post(@RequestBody String content) {
return content;
}
}
}
⚠️ In both examples above, it is required that you specify not only the character encoding (UTF-8) but also the Content-Type
(text/plain).
I believe this may be the expected behavior for StringHttpMessageConverter
; however, I'll clarify that with @rstoyanchev.
Comment From: sbrannen
Hi @lonre,
I confirmed with @rstoyanchev that this is the expected behavior for StringHttpMessageConverter
.
Specifically, the message converters don't take the character encoding of the response into account and instead rely on the HTTP headers.
That's why you need to specify the content type for the request as well as the content type for the response (either via the Accept
header in the request or via the produces
attribute of @PostMapping
).
Hope that clarifies matters for you.
Cheers
Sam