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

SpringBoot POST请求测试中Handler未识别的原因及解决方法

SpringBoot测试问题排查:Handler Type为Null且403 Forbidden错误

问题描述

测试接收JSON格式数据的POST控制器方法时,出现handler: type = null提示,同时返回403 Forbidden错误。

测试代码

@WebMvcTest(QuizController.class)
public class QuizControllerTest {
    @MockBean
    private QuizRepository quizRepository;

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void shouldAddQuestion() throws Exception {
        QuizDTO quizDTO = new QuizDTO(104, "epidemics", 21, 4, "Test Question?", "A", "B", "C", "D", "hint");
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(quizDTO);
        mockMvc.perform(post("/adminadd")
                        .with(user("admin").password("password").roles("USER"))
                .contentType(MediaType.APPLICATION_JSON)
                .content(json))
                .andExpect(status().isCreated())
                .andDo(print());
    }
}

控制器代码

@PostMapping("/adminadd")
public QuizDTO addQuestion(@RequestBody QuizDTO quizDTO) {
    return quizRepo.addQuestion(quizDTO);
}

错误信息

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /adminadd
       Parameters = {}
          Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"175"]
             Body = {"type":"epidemics","questionIndex":21,"choiceNum":4,"question":"Test Question?","choiceA":"A","choiceB":"B","choiceC":"C","choiceD":"D","correct":null,"hint":"hint","id":104}
    Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@5bde6148, SPRING_SECURITY_CONTEXT=SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=admin, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]], Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_USER]]]}

Handler:
             Type = null
MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

问题原因分析

1. handler: type = null的核心原因

  • 控制器未被识别为请求处理器:你的控制器代码仅添加了@PostMapping方法注解,但缺少@RestController或@Controller类级注解,导致@WebMvcTest无法扫描到该类作为Spring MVC控制器Bean,自然找不到对应请求的处理器。
  • 请求路径不匹配:如果控制器类上存在@RequestMapping前缀(比如@RequestMapping("/api")),而测试请求用的是/adminadd,会导致路径不匹配,无法定位到处理器。
  • Security拦截优先:403权限拦截发生在请求映射匹配之前,导致请求从未到达控制器匹配阶段,因此显示处理器类型为null。

2. 403 Forbidden的原因

测试中使用的用户角色是USER,但/adminadd接口大概率需要更高权限(比如ADMIN角色),而你的Security配置可能限制了该路径仅允许ADMIN角色访问,因此触发权限拦截。

解决方案

步骤1:修复控制器注解

给QuizController添加@RestController注解(如果是返回JSON的REST接口),确保Spring能识别它为控制器:

@RestController // 添加类级注解
public class QuizController {
    @Autowired
    private QuizRepository quizRepo;

    @PostMapping("/adminadd")
    public QuizDTO addQuestion(@RequestBody QuizDTO quizDTO) {
        return quizRepo.addQuestion(quizDTO);
    }
}

步骤2:修正测试用户角色

如果/adminadd接口需要ADMIN权限,测试中使用的用户角色要改为ADMIN:

mockMvc.perform(post("/adminadd")
                .with(user("admin").password("password").roles("ADMIN")) // 替换为ADMIN角色
                .contentType(MediaType.APPLICATION_JSON)
                .content(json))

步骤3:验证请求路径一致性

检查控制器类是否有@RequestMapping前缀,比如:

@RestController
@RequestMapping("/api") // 假设存在路径前缀
public class QuizController {
    @PostMapping("/adminadd")
    public QuizDTO addQuestion(@RequestBody QuizDTO quizDTO) {
        return quizRepo.addQuestion(quizDTO);
    }
}

此时测试请求路径要改为/api/adminadd:

mockMvc.perform(post("/api/adminadd") ... )

步骤4:(可选)调试时关闭Security拦截

如果需要快速验证控制器逻辑,可在测试中关闭所有过滤器(包括CSRF和权限拦截):

@WebMvcTest(QuizController.class)
@AutoConfigureMockMvc(addFilters = false) // 仅用于调试,正式测试建议保留权限验证
public class QuizControllerTest {
    // ... 测试代码
}

步骤5:优化ObjectMapper使用

测试中已经通过@Autowired注入了ObjectMapper,无需重新实例化:

// 移除这句:ObjectMapper objectMapper = new ObjectMapper();
String json = this.objectMapper.writeValueAsString(quizDTO);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:40:18