1. 引言

在 Spring Boot 应用中验证 Boolean 类型是常见需求,本文将深入探讨多种验证方案。我们将覆盖控制器层和服务层的验证实现,包括程序化验证、自定义 JSON 反序列化器以及 Bean 验证注解的使用。

2. 程序化验证

Boolean 类提供了两个核心方法用于创建实例:Boolean.valueOf()Boolean.parseBoolean()

  • Boolean.valueOf() 接受 String 或 boolean 值,根据输入返回 Boolean 对象
  • Boolean.parseBoolean() 仅接受 String 值

⚠️ 注意:这些方法不区分大小写,例如 "true"、"True"、"TRUE" 等均有效。

通过单元测试验证 String 到 Boolean 的转换:

@Test
void givenInputAsString_whenStringToBoolean_thenValidBooleanConversion() {
    assertEquals(Boolean.TRUE, Boolean.valueOf("TRUE"));
    assertEquals(Boolean.FALSE, Boolean.valueOf("false"));
    assertEquals(Boolean.TRUE, Boolean.parseBoolean("True"));
}

验证原始 boolean 到 Boolean 包装类的转换:

@Test
void givenInputAsboolean_whenbooleanToBoolean_thenValidBooleanConversion() {
    assertEquals(Boolean.TRUE, Boolean.valueOf(true));
    assertEquals(Boolean.FALSE, Boolean.valueOf(false));
}

3. 自定义 Jackson 反序列化器验证

Spring Boot API 常处理 JSON 数据,我们可通过自定义反序列化器验证 JSON 到 Boolean 的转换。考虑一个场景:用 + 表示 true,- 表示 false。

自定义反序列化器实现:

public class BooleanDeserializer extends JsonDeserializer<Boolean> {
    @Override
    public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        String value = parser.getText();
        if (value != null && value.equals("+")) {
            return Boolean.TRUE;
        } else if (value != null && value.equals("-")) {
            return Boolean.FALSE;
        } else {
            throw new IllegalArgumentException("Only values accepted as Boolean are + and -");
        }
    }
}

4. Bean 验证注解

Bean 验证约束是另一种常用验证方式,需添加 spring-boot-starter-validation 依赖。Boolean 字段可用三个注解:

  • @NotNull:Boolean 字段为 null 时报错
  • @AssertTrue:Boolean 字段为 false 时报错
  • @AssertFalse:Boolean 字段为 true 时报错

⚠️ 踩坑提示@AssertTrue@AssertFalse 将 null 视为有效值!若需强制非空,必须配合 @NotNull 使用。

5. Boolean 验证示例

结合 Bean 约束和自定义反序列化器,创建包含四个 Boolean 参数的 BooleanObject

public class BooleanObject {

    @NotNull(message = "boolField cannot be null")
    Boolean boolField;

    @AssertTrue(message = "trueField must have true value")
    Boolean trueField;

    @NotNull(message = "falseField cannot be null")
    @AssertFalse(message = "falseField must have false value")
    Boolean falseField;

    @JsonDeserialize(using = BooleanDeserializer.class)
    Boolean boolStringVar;

    //getters and setters
}

6. 控制器层验证

通过 RequestBody 传递对象时,使用 @Valid 注解触发验证:

@RestController
public class ValidationController {

    @Autowired
    ValidationService service;

    @PostMapping("/validateBoolean")
    public ResponseEntity<String> processBooleanObject(@RequestBody @Valid BooleanObject booleanObj) {
        return ResponseEntity.ok("BooleanObject is valid");
    }

    @PostMapping("/validateBooleanAtService")
    public ResponseEntity<String> processBooleanObjectAtService() {
        BooleanObject boolObj = new BooleanObject();
        boolObj.setBoolField(Boolean.TRUE);
        boolObj.setTrueField(Boolean.FALSE);
        service.processBoolean(boolObj);
        return ResponseEntity.ok("BooleanObject is valid");
    }
}

验证失败时 Spring 抛出 MethodArgumentNotValidException,通过 ControllerAdvice 处理异常:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public String handleValidationException(MethodArgumentNotValidException ex) {
        return ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(e -> e.getDefaultMessage())
            .collect(Collectors.joining(","));
    }

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public String handleIllegalArugmentException(IllegalArgumentException ex) {
        return ex.getMessage();
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public String handleConstraintViolationException(ConstraintViolationException ex) {
       return ex.getMessage();
    }
}

测试类结构:

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = ValidationController.class)
class ValidationControllerUnitTest {

    @Autowired
    private MockMvc mockMvc;

    @TestConfiguration
    static class EmployeeServiceImplTestContextConfiguration {
        @Bean
        public ValidationService validationService() {
            return new ValidationService() {};
        }
    }

    @Autowired
    ValidationService service;
}

6.1. 验证 @NotNull 注解

传递 null 值时返回 400 错误:

@Test
void whenNullInputForBooleanField_thenHttpBadRequestAsHttpResponse() throws Exception {
    String postBody = "{\"boolField\":null,\"trueField\":true,\"falseField\":false,\"boolStringVar\":\"+\"}";

    mockMvc.perform(post("/validateBoolean").contentType("application/json")
            .content(postBody))
        .andExpect(status().isBadRequest());
}

6.2. 验证 @AssertTrue 注解

传递 false 值时返回错误消息:

 @Test
 void whenInvalidInputForTrueBooleanField_thenErrorResponse() throws Exception {
     String postBody = "{\"boolField\":true,\"trueField\":false,\"falseField\":false,\"boolStringVar\":\"+\"}";

     String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
             .content(postBody))
         .andReturn()
         .getResponse()
         .getContentAsString();

     assertEquals("trueField must have true value", output);
 }

注意:单独使用 @AssertTrue 时 null 值有效:

 @Test
 void whenNullInputForTrueBooleanField_thenCorrectResponse() throws Exception {
    String postBody = "{\"boolField\":true,\"trueField\":null,\"falseField\":false,\"boolStringVar\":\"+\"}";

    mockMvc.perform(post("/validateBoolean").contentType("application/json")
                    .content(postBody))
            .andExpect(status().isOk());
}

6.3. 验证 @AssertFalse 注解

传递 true 值时返回错误消息:

 @Test
 void whenInvalidInputForFalseBooleanField_thenErrorResponse() throws Exception {
     String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":true,\"boolStringVar\":\"+\"}";

     String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
             .content(postBody))
         .andReturn()
         .getResponse()
         .getContentAsString();

     assertEquals("falseField must have false value", output);
 }

✅ 配合 @NotNull 时 null 值会触发验证:

 @Test
 void whenNullInputForFalseBooleanField_thenHttpBadRequestAsHttpResponse() throws Exception {
    String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":null,\"boolStringVar\":\"+\"}";

    mockMvc.perform(post("/validateBoolean").contentType("application/json")
                    .content(postBody))
            .andExpect(status().isBadRequest());
 }

6.4. 验证自定义 JSON 反序列化器

传递非法值时触发错误:

@Test
void whenInvalidBooleanFromJson_thenErrorResponse() throws Exception {
    String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":false,\"boolStringVar\":\"plus\"}";

    String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
            .content(postBody))
        .andReturn()
        .getResponse()
        .getContentAsString();

    assertEquals("Only values accepted as Boolean are + and -", output);
}

✅ 合法值通过验证:

 @Test
 void whenAllBooleanFieldsValid_thenCorrectResponse() throws Exception {
     String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":false,\"boolStringVar\":\"+\"}";

     String output = mockMvc.perform(post("/validateBoolean").contentType("application/json")
             .content(postBody))
         .andReturn()
         .getResponse()
         .getContentAsString();

     assertEquals("BooleanObject is valid", output);
 }

7. 服务层验证

在服务层使用 @Validated@Valid 组合触发验证:

@Service
@Validated
public class ValidationService {

    public void processBoolean(@Valid BooleanObject booleanObj) {
        // further processing
    }
}

⚠️ 关键区别:服务层验证失败抛出 ConstraintViolationException,返回 HTTP 500 状态码。

最佳实践:服务层验证适用于控制器层创建/修改后传递给服务层处理的对象。

测试服务层验证:

@Test
void givenAllBooleanFieldsValid_whenServiceValidationFails_thenErrorResponse() throws Exception {
    mockMvc.perform(post("/validateBooleanAtService").contentType("application/json"))
        .andExpect(status().isInternalServerError());
}

8. 总结

我们掌握了在 Spring Boot 中验证 Boolean 类型的三种核心方案:

  1. 程序化验证(Boolean.valueOf()/parseBoolean()
  2. 自定义 JSON 反序列化器
  3. Bean 验证注解(@NotNull/@AssertTrue/@AssertFalse

通过控制器层和服务层的实战示例,覆盖了从基础验证到复杂场景的完整解决方案。合理选择验证策略能有效提升 API 的健壮性,避免脏数据进入业务逻辑。


原始标题:Validate Boolean Type in Spring Boot | Baeldung