参数验证是一个应用中必不可少的一部分操作,参数验证又可以分为前端验证和后端验证。如果没有参数验证的话,我们的逻辑可能就会报错,例如:空指针异常,更严重的可能造成系统的瘫痪。 参数验证又可以分为前端验证和后端验证,前端验证可以通过各种手段进行跳过,例如直接调用接口等等。为了防护系统出现各种异常,后端验证是不可取少的一部分。本文将主要描述使用Spring Boot项目的时候,后端如何进行参数验证。
我们主要是使用javax.validation.constraints和org.hibernate.validator.constraints包下的注解进行参数验证。常见的注解如下:
@NotNull:表示参数不能为Null,如果为空的话,就无法通过验证@Max:一般用于数字,表示不能超过最大值例1:希望name不能为空,age不能大于100的限制条件。
@RestController @Validated public class TestController { @GetMapping("/testget") public JSONObject testget(@NotNull(message = "姓名不能为空") String name, @Max(value = 100, message = "不能大于100") Integer age) { JSONObject jsonObject = new JSONObject(); return jsonObject; } }如果我们访问localhost:8080/testget或者localhost:8080/testget?age=101或者localhost:8080/testget?age=1,就会抛出一个ConstraintViolationException的异常信息。前端就会收到一个500的错误信息。我们可以进行异常统一捕获,在后面进行描述,从而向前端返回统一的错误信息。
我们使用Restful的格式传递参数。参数使用JSON格式传递。 使用请求体中携带参数的方式主要有三种形式。
下面是使用的实体:包含一个嵌套实体的ChildDTO类
public class AreaDTO { @NotNull(groups = {UpdateValidation.class}, message = "区域ID不能为空") private Long id; @NotNull(message = "父级ID不能为空", groups = {AddValidation.class}) private Long parentId; @NotNull(message = "片区名称不能为空") private String name; @Valid private ChildDTO childDTO; } public class ChildDTO { @NotNull(message = "不能为空") private String childName; @NotNull(groups = {AddValidation.class}, message = "不能为空") private Long childId; @Max(groups = {UpdateValidation.class}, value = 100 ,message = "不能为空") @Min(groups = {AddValidation.class}, value = 50, message = "最小值") private Long age; } //分组一 public interface AddValidation { } //分组二 public interface UpdateValidation { }例2:
@PostMapping("/testpost") public JSONObject testpost(@Validated @RequestBody AreaDTO areaDTO) { JSONObject jsonObject = new JSONObject(); return jsonObject; }例3
@PostMapping("/testpost1") public JSONObject testpost1(@Validated(value = {AddValidation.class}) @RequestBody AreaDTO areaDTO) { JSONObject jsonObject = new JSONObject(); return jsonObject; }上面两个例子中,第一个例子使用了默认的分组(即Default),而第二个例子使用了AddValidation的分组。我们在添加验证条件的时候,可以为其指定分组限制,使用groups属性,例如:@NotNull(message = "父级ID不能为空", groups = {AddValidation.class}),这个验证限制只有在@Validated(value = {AddValidation.calss})才会起作用。只有符合验证条件的分组才会进行验证。
例如:例2中,没有添加分组,默认使用Default分组,就会验证AreaDTO.name,AreaDTO.ChildDTO.childName两个属性。例3中,添加了AddValidation的验证分组,就会验证AreaDTO.parentId,AreaDTO.ChildDTO.childId,AreaDTO.ChildDTO.age三个属性。
当无法通过验证条件的时候,就会抛出一个MethodArgumentNotValidException的异常信息。里面包含了所有所有没有通过验证的属性。
注意:没有添加分组,则默认使用Default的分组。
例4
@PostMapping("/testpost4") public JSONObject testpost4(@Validated @RequestBody AreaDTO areaDTO, BindingResult result) { //手动验证是否有没有通过的参数 if (result.hasErrors()) { for (FieldError fieldError : result.getFieldErrors()) { System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage()); } //这里返回我们自定义的返回结果 } JSONObject jsonObject = new JSONObject(); return jsonObject; }例5:
@Autowired private Validator validator; @PostMapping("/testpost5") public JSONObject testpost5(@RequestBody AreaDTO areaDTO) { Set<ConstraintViolation<AreaDTO>> validate = validator.validate(areaDTO, AddValidation.class, Default.class); if (!validate.isEmpty()) { String message = validate.iterator().next().getMessage(); logger.error(message); } JSONObject jsonObject = new JSONObject(); return jsonObject; }到此,就完成了参数验证的全部内容。各种方式各有不同。
我们使用@RestControllerAdvice的注解,标识这个类为控制器的增强类。
默认情况下,Validator会验证所有需要验证的参数。如果有多个错误,就会出现多个字段异常信息。如果希望,当出现一个参数异常的时候,直接退出,不在继续验证其他参数的正确性。需要配置快速退出
@Configuration public class ValidateConfig { @Bean public Validator validator() { ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class) .configure().failFast(true).buildValidatorFactory(); return validatorFactory.getValidator(); } }