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) in ConfigurableMockMvcBuilder and use it to configure the default character encoding in the underlying MockHttpServletResponse.

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