【问题标题】:Empty Exception Body in Spring MVC TestSpring MVC 测试中的空异常体
【发布时间】:2015-04-04 04:31:17
【问题描述】:

我在尝试让 MockMvc 在响应正文中包含异常消息时遇到问题。我有一个控制器如下:

@RequestMapping("/user/new")
public AbstractResponse create(@Valid NewUserParameters params, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) throw BadRequestException.of(bindingResult);
    // ...
}

BadRequestException 看起来像这样:

@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "bad request")
public class BadRequestException extends IllegalArgumentException {

    public BadRequestException(String cause) { super(cause); }

    public static BadRequestException of(BindingResult bindingResult) { /* ... */ }

}

我对@9​​87654324@ 控制器运行以下测试:

@Test
public void testUserNew() throws Exception {
    getMockMvc().perform(post("/user/new")
            .param("username", username)
            .param("password", password))
            .andDo(print())
            .andExpect(status().isOk());
}

打印以下输出:

  Resolved Exception:
                Type = controller.exception.BadRequestException

        ModelAndView:
           View name = null
                View = null
               Model = null

            FlashMap:

MockHttpServletResponse:
              Status = 400
       Error message = bad request
             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 = []

有人知道为什么print() 输出中缺少Body 吗?

编辑:我没有使用任何自定义异常处理程序,并且在我运行服务器时代码按预期工作。也就是说,运行应用程序并向服务器发出相同的请求会返回

{"timestamp":1423076185822,
 "status":400,
 "error":"Bad Request",
 "exception":"controller.exception.BadRequestException",
 "message":"binding failed for field(s): password, username, username",
 "path":"/user/new"}

正如预期的那样。因此,我想MockMvc 存在问题。它以某种方式错过了捕获异常的message 字段,而常规应用程序服务器的默认异常处理程序按预期工作。

【问题讨论】:

    标签: spring spring-mvc exception-handling spring-mvc-test


    【解决方案1】:

    在为该问题打开ticket 后,我被告知正文中的错误消息由 Spring Boot 处理,它在 Servlet 容器级别配置错误映射,并且由于 Spring MVC Test 使用模拟 Servlet 请求运行/response,没有这样的错误映射。此外,他们建议我至少创建一个 @WebIntegrationTest 并坚持使用 Spring MVC 测试来测试我的控制器逻辑。

    最终,我决定使用我自己的自定义异常处理程序,并像以前一样坚持使用MockMvc

    @ControllerAdvice
    public class CustomExceptionHandler {
    
        @ExceptionHandler(Throwable.class)
        public @ResponseBody
        ExceptionResponse handle(HttpServletResponse response, Throwable throwable) {
            HttpStatus status = Optional
                    .ofNullable(AnnotationUtils.getAnnotation(throwable.getClass(), ResponseStatus.class))
                    .map(ResponseStatus::value)
                    .orElse(HttpStatus.INTERNAL_SERVER_ERROR);
            response.setStatus(status.value());
            return new ExceptionResponse(throwable.getMessage());
        }
    
    }
    
    @Data
    public class ExceptionResponse extends AbstractResponse {
    
        private final long timestamp = System.currentTimeMillis();
    
        private final String message;
    
        @JsonCreator
        public ExceptionResponse(String message) {
            checkNotNull(message, "message == NULL");
            this.message = message;
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      这可能意味着您要么没有处理异常,要么真的让主体为空。要处理异常,请在控制器中添加错误处理程序

      @ExceptionHandler
      public @ResponseBody String handle(BadRequestException e) {
          return "I'm the body";
      }
      

      如果您使用的是 3.2 或更高版本,则使用全局错误处理程序

      @ControllerAdvice
      public class GlobalExceptionHandler {
      
          @ExceptionHandler
          public @ResponseBody String handleBadRequestException(BadRequestException ex) {
              return "I'm the body";
          }
      }
      

      主体将被填充,您应该使用错误消息填充它

      【讨论】:

      • 我正在使用默认的异常处理程序。问题是,MockMvc 不捕获 message 字段,而常规应用程序服务器的默认异常处理程序会捕获。 (请参阅我对问题的编辑。)
      【解决方案3】:

      更新的解决方案:

      如果您不想进行完整的集成测试,但仍想确保消息符合预期,您仍然可以执行以下操作:

      String errorMessage = getMockMvc()
                              .perform(post("/user/new"))
                              ...
                              .andReturn().getResolvedException().getMessage();
      
      assertThat(errorMessage, is("This is the error message!");
      

      【讨论】:

        猜你喜欢
        • 2013-08-11
        • 2018-08-27
        • 1970-01-01
        • 2021-10-13
        • 2015-05-10
        • 1970-01-01
        • 2020-02-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多