简单校验
在后端开发的过程中,验证前端参数的合法性是一个必不可少的步骤。但是参数验证会产生大量的样板代码,导致代码可读性差。使用 validator-api
可以简洁优雅的验证参数。我们来看一段代码:
1 2 3 4 5 6 7
| @GetMapping public ResponseEntity index(@RequestParam("userOrderId") String userOrderId) { if (null == userOrderId || "".equals(userOrderId)) return ResponseEntity.badRequest().build(); return ResponseEntity.ok().build(); }
|
虽然这样做也能起到校验的作用,但是当参数比较多,参数比较复杂的时候会写大量的校验代码,既麻烦又影响可读性。SpringBoot 可以帮我们简化校验,spring-boot-starter-web
中默认集成了 hibernate-validator
包,它提供了一系列验证各种参数的方法,所以说 SpringBoot 已经帮我们想好要怎么解决这个问题了。上面的例子,可以简化成如下代码:
1 2 3 4
| @GetMapping public ResponseEntity index(@RequestParam("userOrderId") @NotBlank String userOrderId) { return ResponseEntity.ok().build(); }
|
@NotBlank
注解用来做非空判断,这样一来,代码量少了一大截,可读性也提高了。
不仅仅可以对包装类型做校验,如果接收参数是自定义对象,我们也是可以做校验的,例如:
1 2 3 4 5 6 7 8
| @Data public class User { @Email(message = "错误的邮箱格式") private String email;
@Size(min = 6, max = 30, message = "密码长度应当在 6 ~ 30 个字符之间") private String password; }
|
1 2 3 4
| @GetMapping public ResponseEntity index(@Validated @RequestBody User user) { return ResponseEntity.ok().build(); }
|
@Validated
注解开启对对象的校验。还有一些其他的注解,像 @Min @Max @Length(min = 1, max = 2) 等等,更多的注解可以去 官方文档 查看。
自定义校验
但是在真实的业务逻辑中,这些基本的校验是不够的。更多的参数需要在数据库中进行对比,才能确定参数的合法性。比如在用户查看订单的时候,客户端传递过来一个订单的ID,这时候我们不仅要做非空验证,还要从数据库去判断 订单ID是否存在,这时候现有的验证注解就无法满足我们的需求了。我们需要实现一个 自定义的注解,用于验证订单ID是否存在,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = UserOrderConstraintValidator.class) @Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER }) public @interface UserOrderValidation { String message(); Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
|
想让自定义验证注解生效,需要实现 ConstraintValidator
接口。接口的第一个参数是 * 自定义注解类型**,第二个参数是 被注解字段的类型。这里因为订单ID 是 String 类型,我们第二个参数定义为 String 就可以了,需要提到的一点是 ConstraintValidator
接口的实现类无需添加 @Component
它在启动的时候就已经被加载到容器中了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Slf4j public class UserOrderConstraintValidator implements ConstraintValidator<UserOrderValidation, String> {
@Resource private UserOrderService userOrderService;
@Override public void initialize(UserOrderValidation constraintAnnotation) { log.info("initialize constraint"); }
@Override public boolean isValid(String value, ConstraintValidatorContext context) { return StringUtils.isNotEmpty(userOrderId) && userOrderService.exists(userOrderId); } }
|
自定义校验注解和默认实现的注解的使用方法是一致的,例如我们在控制层去校验请求的订单ID是否在数据库中存在。
1 2 3 4 5 6 7 8 9
| @RestController @RequestMapping("/user/orders") public class UserOrderController { @GetMapping("/{id}") public ResponseEntity<?> show(@PathVariable("id") @UserOrderValidation String userOrderId) { return ResponseEntity.ok().build(); } }
|