SpringBootTest MockMvc测试HttpMediaTypeNotSupportedException问题
问题根因
两个报错和你设置的Content-Type、Accept请求头无关,是测试配置缺失导致的独立问题:
415 Unsupported Media Type 成因
@SpringBootTest(classes = Controller.class) 配置只会加载你指定的Controller单个Bean,不会触发Spring Web层的自动配置流程,负责解析JSON请求的MappingJackson2HttpMessageConverter(Jackson消息转换器)不会被注册。没有这个转换器时,Spring无法识别application/json类型的请求,哪怕请求头配置正确也会判定媒体类型不支持,返回415。日志里响应头Accept字段列出的所有支持媒体类型里没有application/json,也能佐证这一点。
403 Forbidden 成因
移除addFilters = false后,Spring Security的过滤器链会生效,默认开启的CSRF防护会拦截所有没有携带合法CSRF Token的POST类修改请求,直接返回403,请求根本不会进入控制器方法,所以日志里Handler字段显示为null。
修复方案
按以下步骤调整即可解决问题:
- 替换测试类的上下文加载注解,做Web层接口测试优先使用
@WebMvcTest(Controller.class)代替原有的@SpringBootTest(classes = Controller.class)。@WebMvcTest是Spring Boot专门提供的Web层切片测试注解,会自动配置MockMvc、JSON消息转换器、所有Web层相关组件,加载速度远快于全量上下文的@SpringBootTest,你已经用@MockBean标注的Service层Mock对象也能正常注入。
如果需要加载完整应用上下文做集成测试,直接去掉@SpringBootTest的classes属性,让Spring自动扫描主启动类加载全量配置即可。 - 解决CSRF拦截问题,引入
spring-security-test依赖后,在构建MockMvc请求时追加.with(SecurityMockMvcRequestPostProcessors.csrf())携带合法CSRF Token即可,这种方式和生产环境的校验逻辑一致,测试结果更准确。如果测试场景不需要走安全过滤器链,也可以保留@AutoConfigureMockMvc(addFilters = false)配置,跳过所有过滤器校验,前提是已经替换为@WebMvcTest或正确加载了Web层自动配置解决415问题。 - 可选优化:控制器方法里手动校验
Errors的逻辑属于重复代码,Spring默认会在参数校验失败时抛出MethodArgumentNotValidException,可以通过@RestControllerAdvice做全局异常拦截统一返回400响应,不需要在每个接口方法内重复写判断逻辑。
修正后的测试类参考代码
@WebMvcTest(Controller.class) class ControllerTest { private ObjectMapper objectMapper; @MockBean private Service service; @Autowired private MockMvc mockMvc; @BeforeEach void setUp() { objectMapper = new ObjectMapper(); } @Test void createOrAdd_shouldReturnErrorResponseOnInvalidInput() throws Exception { CreateAgencyRequest request = CreateAgencyRequest.builder() .name("name<script>") .primaryEmail("test@mail.com") .build(); mockMvc.perform(MockMvcRequestBuilders.post("/api/create") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString(request)) .characterEncoding("utf-8") .with(SecurityMockMvcRequestPostProcessors.csrf())) .andExpect(MockMvcResultMatchers.status().isBadRequest()); } }
注意:代码里的请求实体类名需要和你实际定义的类名保持一致,示例中使用了你提供的
CreateAgencyRequest作为实体类名。如果静态导入SecurityMockMvcRequestPostProcessors.csrf方法,请求构建部分可以直接简写为.with(csrf())。
内容的提问来源于stack exchange,提问作者tusharRawat
相关产品推荐
相关产品推荐

