You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot MockMvc测试中俄语字符编码异常问题

MockMvc集成测试中俄语字符乱码问题

在使用MockMvc为Spring Boot应用编写集成测试时,遇到请求/响应体中的俄语字符乱码问题。Swagger或Postman测试应用时一切正常,但MockMvc测试中:

  • GET请求返回的响应字符变为乱码(如ТРМ-001变成ТРÐ-001)
  • POST请求的请求体俄语字符显示为??????,导致后端校验失败返回400

已尝试在测试类属性、ObjectMapper、MockMvc中设置编码,修改迁移文件,均无效。

测试类配置

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = InfoApplicationConfig.class)
@TestPropertySource(properties = {"spring.config.location=classpath:application-it.yml"})
@AutoConfigureMockMvc
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class TerminalControllerIT {

    @Autowired
    MockMvc mockMvc;

    @LocalServerPort
    private int port;

    private String baseUrl = "http://localhost";

    private final String validTerminalId = "eee00000-0000-0000-0000-000000000000";

    private final String invalidTerminalId = "abc00000-0000-0000-0000-000000123456";

    private NewTerminalDto testSubject;

    @Autowired
    private static ObjectMapper objectMapper;

    @Autowired
    private TerminalRepository terminalRepository;

    @BeforeEach
    public void setUp() {
        testSubject = NewTerminalDto.builder().country("Россия").region("Москва").city("Москва")
                .street("Ленина").buildingNumber("12A")
                .roomNumber("101").terminalCoordinate("55.7558,37.6176")
                .postCode("123456").terminalNumber("123")
                .isClosed(false).cashDepositWithdrawal(false).moneyTransfer(true).payment(true).nfc(true)
                .banknotesPerPack(100)
                .biometrics(false).encashmentService(true).cashDeposit(true)
                .openingTime("08:00")
                .closingTime("18:00")
                .build();
        baseUrl = baseUrl.concat(":" + port).concat("/api/v1/info-service/terminals");
    }
}

测试1:GET请求响应乱码

测试代码

@Test
@DisplayName("Should return terminal by id")
void getTerminalInfoByIdPositiveTest() throws Exception {
    MvcResult result = mockMvc.perform(get(baseUrl + "/" + validTerminalId)
                    .characterEncoding("UTF-8")
                    .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andReturn();

    String jsonResponse = result.getResponse().getContentAsString();
    TerminalGetResponse actualResponse = objectMapper.readValue(jsonResponse, TerminalGetResponse.class);

    assertNotNull(actualResponse);
    assertNotNull(actualResponse.getTerminalNumber());
    assertNotNull(actualResponse.getRegion());
    assertEquals("ТРМ-001", actualResponse.getTerminalNumber());
    assertEquals("Москва", actualResponse.getRegion());
}

测试结果

Expected :ТРМ-001
Actual   :ТРÐ-001

测试2:POST请求体乱码

测试代码

@Test
@DisplayName("Adding branch")
void addBranch() throws Exception {
    BranchPayload body = BranchStubs.createBranchPayload(0);

    mockMvc.perform(post(baseUrl)
                    .content(objectMapper.writeValueAsBytes(body))
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                    .characterEncoding("UTF-8"))
            .andExpect(status().isOk());

    BankBranchesModel bankBranchesModel = BankBranchesModel.builder().branchNumber(body.getBranchNumber()).build();

    assertNotNull(bankBranchesRepository.findOne(Example.of(bankBranchesModel)).orElse(null));
}

测试结果

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /api/v1/info-service/branches
       Parameters = {}
          Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"907"]
             Body = {"branchNumber":"8500","country":"??????","region":"?????????? ???????","city":"????????","street":"???????????","buildingNumber":"5","postCode":"117997","branchCoordinate":"55.696225, 37.544539","ramp":true,"phoneNumber":"74955555550","isClosed":false,"openingTime":"00:00","closingTime":"23:55","dayOfWeek":["???????????","???????","?????","???????","???????"],"currencyExchange":true,"foreignCurrency":true,"moneyTransfer":true,"cashWithdrawal":true,"payment":true,"replenishCard":true,"replenishAccount":true,"hasDeposit":true,"hasCredit":true,"consultation":true,"insurance":true,"bik":"044525220","kpp":"773643002","inn":"7707083890","paymentAccount":"40702810562000000000","correspondentAccount":"30101810400000000225","bankNameFull":"?? «FinTech Bank»","okpo":"09610477","ogrn":"1027700057410","swift":"LIBBRUMM007"}
    Session Attrs = {}
...
MockHttpServletResponse:
           Status = 400
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"bankNameFull":"must match \"^(?!\\s*$)[0-9A-Za-z?-??-? !\"#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_{|}~]{1,30}$\""}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

解决方案

1. 全局配置MockMvc默认编码

通过自定义MockMvc Bean,强制设置响应编码为UTF-8:

@Bean
public MockMvc mockMvc(WebApplicationContext webApplicationContext) {
    return MockMvcBuilders.webAppContextSetup(webApplicationContext)
            .defaultResponseCharacterEncoding(StandardCharsets.UTF_8)
            .build();
}

或者直接在@AutoConfigureMockMvc注解中指定字符集:

@AutoConfigureMockMvc(charset = "UTF-8")

2. 确保ObjectMapper使用UTF-8编码

配置ObjectMapper时明确指定UTF-8,避免序列化/反序列化时编码不一致:

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    // 其他配置...
    objectMapper.setCharset(StandardCharsets.UTF_8);
    objectMapper.getFactory().setCharset(StandardCharsets.UTF_8);
    return objectMapper;
}

3. 优化GET请求的编码设置

在GET请求中,除了设置请求编码,还要指定接受的响应编码:

MvcResult result = mockMvc.perform(get(baseUrl + "/" + validTerminalId)
                .characterEncoding("UTF-8")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON.withCharset(StandardCharsets.UTF_8)))
        .andExpect(status().isOk())
        .andReturn();

4. 修改POST请求的内容传递方式

避免使用writeValueAsBytes,改用字符串形式传递请求体,并明确指定内容类型的编码:

mockMvc.perform(post(baseUrl)
                .content(objectMapper.writeValueAsString(body))
                .contentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8")
                .characterEncoding("UTF-8"))
        .andExpect(status().isOk());

5. 设置JVM启动参数

确保测试运行时JVM默认编码为UTF-8,添加启动参数:

-Dfile.encoding=UTF-8

内容的提问来源于stack exchange,提问作者D Bystrov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:04:56