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

Spring Boot:添加Authorization头后MockMVC单元测试返回404

问题排查:无效JWT带Bearer前缀返回404的原因及修复

核心现象

测试无效JWT场景时:

  • 未添加Bearer前缀:请求被Spring Security拦截,返回401(符合预期)
  • 添加Bearer前缀:请求返回404(不符合预期,应为401)

关键原因分析

  1. MockMvc构建逻辑错误:测试类中使用standaloneSetup(context)传入WebApplicationContext,而standaloneSetup设计用于单独测试控制器实例,传入上下文会导致路由映射未正确加载,请求无法匹配到控制器接口。
  2. 控制器路由前缀缺失:若BookingController类未添加@RequestMapping("/booking")前缀,方法上的@PostMapping("")实际对应根路径/,但测试请求的是/booking,导致路径不匹配返回404。
  3. 测试逻辑混淆:Mock的bookingService抛出异常的逻辑不会被执行,因为请求根本没到达控制器层(404说明路由未匹配)。

修复步骤

1. 修正MockMvc构建方式

将standaloneSetup(context)改为传入控制器实例,确保路由映射正确加载:

@BeforeEach
public void setup() {
    this.mockMvc = MockMvcBuilders.standaloneSetup(bookingController)
            .setControllerAdvice(new GeneralBookingException(), new UnauthorizedException())
            .apply(springSecurity(springSecurityFilterChain))
            .build();
}

若需基于WebApplicationContext测试,改用webAppContextSetup(需配合@MockBean替换服务层):

@BeforeEach
public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
            .apply(springSecurity(springSecurityFilterChain))
            .build();
}

2. 补充控制器路由前缀

确认BookingController类上存在@RequestMapping("/booking")注解,保证请求路径匹配:

@RestController
@RequestMapping("/booking")
public class BookingController {
    // 现有方法代码
    @Operation(summary = "Booking Creation")
    @PostMapping("")
    public Mono<BookingResponse> userBooking(...) {
        // ...
    }
}

3. 调整测试类注解(针对webAppContextSetup场景)

若使用webAppContextSetup,需用@MockBean替代@Mock和@InjectMocks,让Spring自动管理上下文和Mock实例:

@SpringBootTest
@AutoConfigureMockMvc
class BookingControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private BookingService bookingService;

    // 测试方法保持不变
}

4. 验证JWT无效场景的正确逻辑

当修复路由问题后,无效JWT(带Bearer前缀)会被Spring Security的JWT解码器识别为无效,直接返回401,无需依赖服务层抛出异常。可调整测试逻辑,无需Mock服务层:

@Test
public void testUserBooking_Unauthorized() throws Exception {
    BookingCreateRequestDTO request = new BookingCreateRequestDTO();
    request.setClientId("client1");

    mockMvc.perform(post("/booking")
                    .contentType(MediaType.APPLICATION_JSON)
                    .header("Authorization", "Bearer invalid-token")
                    .content(objectToJson(request)))
            .andExpect(status().isUnauthorized());
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:10:54