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

问题描述 投票:0回答:11

升级到新发布的

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();

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

java.lang.AssertionError: Content type 
Expected :application/json;charset=UTF-8
Actual   :application/json

将代码更改为

.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."}]

如何获得UTF-8编码的

content

spring-boot spring-mvc jackson spring-test
11个回答
37
投票

是的。这是 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));
    }
...

27
投票

使用

.getContentAsString(StandardCharsets.UTF_8)
代替
.getContentAsString()
可以解决问题。


11
投票

从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();
}

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

希望这有帮助。


5
投票

按照 black4bird 的答案,您可以通过在测试 Spring 上下文中放置以下

MockMvcBuilderCustomizer
实现来覆盖所有测试的字符编码:

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

如果您不想显式设置 MockMvc,而只需使用

@AutoconfigureMockMvc
,这可能会有所帮助。


2
投票

我正在使用 Spring Boot 1.5.15.RELEASE,并且在编写测试时遇到了同样的问题。

第一个帮助我的解决方案是添加 .characterEncoding("UTF-8")) 像这样:

String content = mockMvc.perform(get("/some-api")
            .contentType(MediaType.APPLICATION_JSON)
            .characterEncoding("UTF-8"))
            .andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
            .andReturn()
            .getResponse()
            .getContentAsString();

我在测试类中使用 StandaloneMockMvcBuilder,因此帮助我的第二个解决方案是创建一个过滤器,例如:

private static class Utf8Filter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        filterChain.doFilter(request, response);
    }
}

然后将其添加到我的测试类中的standaloneSetup方法中,如下所示:

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
    final SomeResource someResource = new SomeResource(someService);
    this.restLankMockMvc = MockMvcBuilders.standaloneSetup(someResource)
        .setCustomArgumentResolvers(pageableArgumentResolver)
        .setControllerAdvice(exceptionTranslator)
        .setConversionService(createFormattingConversionService())
        .setMessageConverters(jacksonMessageConverter)
        .addFilter(new Utf8Filter())
        .build();
}

2
投票

MockMvc 的额外设置,

.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
:

    String content = mockMvc.perform(get("/some-api")
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON_UTF8_VALUE))
        .andExpect(status().isOk())
        .andExpect(content().contentType(MediaType.APPLICATION_JSON))
        .andReturn()
        .getResponse()
        .getContentAsString();

我猜这个问题不是 Spring Boot 的问题,而是 MockMvc 特有的问题。 因此,解决方法必须仅应用于 MockMvc。 (JSON 必须使用 UTF-8 编码。)

相关问题:

MockMvc 中 JSON 响应的 UTF-8 处理不正确 · Issue #23622 · spring-projects/spring-framework


2
投票
要恢复原始行为 (Content-Type=application/json;charset=UTF-8) 并允许测试按原样通过,您可以执行以下操作:

@Configuration public class MyWebConfig implements WebMvcConfigurer { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.defaultContentType(MediaType.APPLICATION_JSON_UTF8); } ...
但是,由于 APPLICATION_JSON_UTF8 已被弃用,并且 Spring 的目的不是包含该字符集,因此最好继续修改您的测试。 black4bird 的解决方案对我有用。



0
投票
如果您使用maven,您可以使用maven-surefire-plugin并以这种方式启用UTF-8编码:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>-Dfile.encoding=UTF-8</argLine> </configuration> </plugin>
    

0
投票
这适用于 Spring Boot 2.6.6

@Autowired private WebApplicationContext wac; private MockMvc mockMvc; @PostConstruct public void init() { mockMvc = MockMvcBuilders.webAppContextSetup(wac) .defaultResponseCharacterEncoding(StandardCharsets.UTF_8) .build(); }
    

0
投票
使用 Spring boot 2.7.14,基于

https://stackoverflow.com/users/8369595/micha%c5%82-kaciuba 的答案(https://stackoverflow.com/a/69380914/246097 )我使用了以下代码:

@Component class MockMvcCharacterEncodingCustomizer implements MockMvcBuilderCustomizer { @Override public void customize(ConfigurableMockMvcBuilder<?> builder) { builder.alwaysDo(result -> result.getResponse().setDefaultCharacterEncoding("UTF-8")); } }
请注意,我使用了 

setDefaultCharacterEncoding

,而不是 
setCharacterEncoding

这样,我就可以像往常一样继续收到

application/json

,并且不需要使用
.getContentAsString(StandardCharsets.UTF_8)

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.