MockMvc 不再使用 Spring Boot 2.2.0.RELEASE 处理 UTF-8 字符

Tim*_*mes 31 spring-mvc spring-test jackson spring-boot

在升级到新发布2.2.0.RELEASE的 Spring Boot 版本后,我的一些测试失败了。看来,MediaType.APPLICATION_JSON_UTF8已弃用并且不再作为未明确指定内容类型的控制器方法的默认内容类型返回。

测试代码如

String content = mockMvc.perform(get("/some-api")
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andReturn()
            .getResponse()
            .getContentAsString();
Run Code Online (Sandbox Code Playgroud)

突然不再工作,因为内容类型不匹配,如下所示

java.lang.AssertionError: Content type 
Expected :application/json;charset=UTF-8
Actual   :application/json
Run Code Online (Sandbox Code Playgroud)

将代码更改为 .andExpect(content().contentType(MediaType.APPLICATION_JSON))暂时解决问题。

但是现在当与content预期的序列化对象进行比较时,如果对象中有任何特殊字符,仍然存在不匹配。似乎该.getContentAsString()方法默认不使用 UTF-8 字符编码(不再使用)。

java.lang.AssertionError: Response content expected:<[{"description":"Er hörte leise Schritte hinter sich."}]> but was:<[{"description":"Er hörte leise Schritte hinter sich."}]>
Expected :[{"description":"Er hörte leise Schritte hinter sich."}]
Actual   :[{"description":"Er hörte leise Schritte hinter sich."}]
Run Code Online (Sandbox Code Playgroud)

我怎样才能进入contentUTF-8 编码?

小智 24

是的。这是 2.2.0 spring-boot 的问题。他们为默认字符集编码设置了弃用。

.getContentAsString(StandardCharsets.UTF_8) - 很好,但在任何响应中都会默认填充 ISO 8859-1。

在我的项目中,我更新了当前创建的转换器:

@Configuration
public class SpringConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.stream()
            .filter(converter -> converter instanceof MappingJackson2HttpMessageConverter)
            .findFirst()
            .ifPresent(converter -> ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(UTF_8));
    }
...
Run Code Online (Sandbox Code Playgroud)

  • 这有点误导,因为它只会导致内容类型标头为“application/json;charset=UTF-8”,这违背了 Spring 最初弃用字符集部分的目的。那么修复测试不是更好吗?但是如果您要使用已弃用的值,我认为正确的方法是使用 ContentNegotiationConfigurer (2认同)

Tim*_*mes 10

使用.getContentAsString(StandardCharsets.UTF_8)而不是.getContentAsString()解决问题。

  • 这对我有用并且是一个非常干净的解决方案。`字符串 json = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);` (3认同)

bla*_*ird 8

从 spring 5.2.0 版本开始,默认编码字符不再是 UTF-8。

要继续使用 UTF-8,必须在 MockMvc 结果的 ServletResponse 中进行设置。要将默认字符编码设置为 UTF-8,请在设置方法中执行以下操作:

@Before
public void setUp() {
   mockMvc = webAppContextSetup(wac).addFilter(((request, response, chain) -> {
                response.setCharacterEncoding("UTF-8");
                chain.doFilter(request, response);
            })).build();
}
Run Code Online (Sandbox Code Playgroud)

然后您可以使用 mockMvc 实例来执行您的请求。

希望这有帮助。


小智 5

根据black4birdMockMvcBuilderCustomizer的答案,您可以通过在测试 Spring 上下文中放置以下实现来覆盖所有测试的字符编码:

@Component
class MockMvcCharacterEncodingCustomizer implements MockMvcBuilderCustomizer {
    @Override
    public void customize(ConfigurableMockMvcBuilder<?> builder) {
        builder.alwaysDo(result -> result.response.characterEncoding = "UTF-8");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不想显式设置 MockMvc,而只想使用@AutoconfigureMockMvc.