首页 > 解决方案 > 在 Spring Boot 控制器中验证 HTTP POST 请求中的请求正文是否为空

问题描述

我正在替换 Spring Boot REST 控制器中对 POST 请求的输入的手动验证。JSR-303 Spring Bean Validation 用于验证请求正文中的实例变量,并且按预期工作。验证请求正文中的对象不为空的推荐方法是什么?

我试过了:

我正在更换:

@PostMapping...
public ResponseEntity<Map<String, Object>> editFoo(
@RequestBody Foo foo, ...) {
   if(foo == null) {
      return (new ResponseEntity<>(headers, HttpStatus.BAD_REQUEST));
   }
}

具有等效的 Bean 验证:

@PostMapping...
public ResponseEntity<Map<String, Object>> editFoo(
@Valid @RequestBody Foo foo, ...) {
   ...
}

我尝试通过以下方式对控制器方法进行单元测试:

// Arrange
    Foo foo = null;
    String requestBody = objectMapper.writeValueAsString(foo);

    // Act + assert
    mockMvc
    .perform(
    post("/end_point")
    .contentType("application/json")
    .content(requestBody))
    .andExpect(status().isBadRequest());

我期望一个 MethodArgumentNotValidException 由 @ControllerAdvice 处理此异常,但在执行单元测试时我得到 HttpMessageNotReadableException 。我的问题:

  1. 是否有必要测试请求正文是否为空?
  2. 如果 1. 为真,那么 Bean Validation 应该如何做到这一点?

标签: springrestspring-bootcontrollerhttprequest

解决方案


看到您的代码,您已经检查了正文是否为空。实际上@RequestBody有一个默认参数required,默认为 true。所以不需要 Bean 验证!

您的主要问题似乎在您的测试中。首先,最好编写一个测试来验证您的端点在 null 上的行为。

但是,在您的测试中,您没有通过 null。您尝试null使用objectMapper. 您正在编写的对象似乎不是有效的 json。因此,当您发送此正文时,Spring 说它无法读取消息,也就是您的请求正文,因为您说它是application/json内容,但其中没有 json。

要测试空正文,只需在测试中发送您的请求,只需删除该.content(requestBody)行即可!

--- 编辑 1 我以为是因为正文而拒绝了该消息,但实际上它似乎对我立即起作用。这是我的控制器和测试,因此您可以与完整代码进行比较:

@RestController()
@RequestMapping("end_point")
public class TestController {

    @PostMapping
    public ResponseEntity<Map<String, Object>> editFoo(@RequestBody Foo foo) {
       // if(foo == null) {
       //     return (new ResponseEntity<>(new HashMap<>(), HttpStatus.BAD_REQUEST));
       // }

        return (new ResponseEntity<>(new HashMap<>(), HttpStatus.OK));
    }
}


@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class TestControllerTest {
    @Autowired
    private MockMvc mvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void test_body_is_null() throws Exception {

        Foo foo = null;
        String requestBody = objectMapper.writeValueAsString(foo);

        // Act + assert
        mvc
            .perform(
                post("/end_point")
                    .contentType("application/json")
                    .content(requestBody))
            .andExpect(status().isBadRequest());
    }
}

这是使用 Spring Boot 2.1.6.RELEASE 制作的

--- 编辑 2 如果您想在此处使用 null 验证作为记录,这里是控制器的片段:

@RestController()
@RequestMapping("end_point")
@Validated
public class TestController {

    @PostMapping
    public ResponseEntity<Map<String, Object>> editFoo(@NotNull @RequestBody(required = false) Foo foo) {

        return (new ResponseEntity<>(new HashMap<>(), HttpStatus.OK));
    }
}

首先,您必须将主体的 required 设置为 false,因为默认值为 true。然后您必须在请求正文和控制器上添加@NotNull注释。@Validated在这里,如果您启动测试,您将看到请求失败并显示:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.ConstraintViolationException: editFoo.foo: must not be null

正如您所说,@ControllerAdvice您可以根据需要映射异常!


推荐阅读