SpringBoot 参数校验

mac2024-04-20  7

介绍

通常我们在写后台接口时,通常都需要校验参数,虽然前台也会校验参数,但是我们不能相信的前台的校验,因为前台参数可以人为篡改,所以后台必须自己校验参数。 在校验中,通常会按如下方式进行校验

if (StringUtils.isEmpty(userInfo.getUsername())) { return "账号不能为空"; } if (StringUtils.isEmpty(userInfo.getRoleId()) || userInfo.getRoleId() > 100 || userInfo.getRoleId() < 1) { return "权限不能为空,并且范围为[1-99]"; } if (age < 1 || age > 150){ return "年龄不合法!" }

这种写法虽然可行,但是太过冗余,所以我们可以使用框架来进行校验,Validation Spingboot中提供Spring Validator和Hibernate Validator是两套Validator,可以混着用,这里我们用Hibernate Validator 常用的用于参数校验的注解如下:

@AssertFalse 所注解的元素必须是Boolean类型,且值为false @AssertTrue 所注解的元素必须是Boolean类型,且值为true @DecimalMax 所注解的元素必须是数字,且值小于等于给定的值 @DecimalMin 所注解的元素必须是数字,且值大于等于给定的值 @Digits 所注解的元素必须是数字,且值必须是指定的位数 @Future 所注解的元素必须是将来某个日期 @Max 所注解的元素必须是数字,且值小于等于给定的值 @Min 所注解的元素必须是数字,且值小于等于给定的值 @Range 所注解的元素需在指定范围区间内 @NotNull 所注解的元素值不能为null @NotBlank 所注解的元素值有内容 @Null 所注解的元素值为null @Past 所注解的元素必须是某个过去的日期 @PastOrPresent 所注解的元素必须是过去某个或现在日期 @Pattern 所注解的元素必须满足给定的正则表达式 @Size 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内 @Email 所注解的元素需满足Email格式

实列

pom.xml

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--参数校验框架--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>

单个参数校验

在controller参数中增加校验注解

@RestController public class LoginController { @RequestMapping("/validate") public String validate(@NotBlank String phone, @Length(min = 3,message = "验证码长度不能小于3位") String code){ return "successs"; } }

实体类参数校验

定义实体类

@Data public class User { //分组校验 @NotNull( groups = {GroupA.class},message = "id不能为空") private Integer id; @NotBlank(message = "用户名不能为空") @Email private String userName; @NotBlank(message = "密码不能为空") @Length(min = 6,message = "密码长度最少6位") private String password; @NotBlank(message = "手机号不能为空") @Phone private String phone; }

定义controller

@RequestMapping("/login") public String login(@Validated LoginForm loginForm){ return "success"; }

分组参数校验

分组参数校验在实体类参数校验中已经实现 @NotNull( groups = {GroupA.class},message = “id不能为空”) 主要是通过group类来实现。 比如id字段在更新参数中需要校验,在插入参数中确不需要校验。

自定义参数校验

虽然框架中提供了许多校验参数,但是有些参数需要我们自己去定义参数校验。 自定义参数步骤: 1.自定义注解 2.自定义校验实现类 3.使用注解 下面这个列子,自定义注解和自定义实现类是写在一起的

import org.springframework.util.StringUtils; import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.regex.Pattern; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented @Constraint(validatedBy = Phone.PhoneValidator.class) public @interface Phone { // 这个地方修改错误提示字符,其他地方不要修改 String message() default "手机号码格式错误"; Class<?>[] groups() default { }; // 用于分组校验 Class<? extends Payload>[] payload() default { }; //自定义实现类 class PhoneValidator implements ConstraintValidator<Phone, String> { static final String regex = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$"; @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { if (StringUtils.isEmpty(s)) return false; return Pattern.matches(regex, s); } } }

定义完注解后就可以在字段中使用注解了。

定义全局异常

在参数中使用注解后, 我们可以通过全局异常拦截不同参数错误,再返回错误信息。 单字段参数校验和实体类字段校验拦截的异常参数类是不同。下面我们分给介绍

单字段全局异常定义

单字段的全局异常类为javax.validation.ConstraintViolationException

@ControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(ConstraintViolationException.class) @ResponseBody public java.lang.String handler(HttpServletRequest request, ConstraintViolationException ex){ StringBuilder sb = new StringBuilder(); Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations(); for (ConstraintViolation<?> constraintViolation : constraintViolations) { sb.append(constraintViolation.getMessage()); } System.out.println(sb.toString()); return sb.toString(); } }

实体类的全局异常

实体类的全局异常类是org.springframework.validation.BindException

@ControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(BindException.class) @ResponseBody public java.lang.String handler( BindException ex){ StringBuilder sb = new StringBuilder(); FieldError fieldError = ex.getFieldError(); sb.append(fieldError.getDefaultMessage()); System.out.println(sb.toString()); return sb.toString(); } }
最新回复(0)