赞
踩
在web开发中,前端的参数校验是为了用户体验,后端的参数校验是为了安全。
参数有两种形式:
一般用于GET请求,会以url string的形式进行传递
一般用于POST请求,可以使用Content-Type来指定不同参数类型
spring-boot可能目前并不支持对参数的验证。
注意:单个参数校验在类上添加注解:@Validated
注解 | 说明 | 备注 |
---|---|---|
@AssertFalse | 所注解的元素必须是Boolean类型,且值为false | |
@AssertTrue | 所注解的元素必须是Boolean类型,且值为true | |
@DecimalMax | 所注解的元素必须是数字,且值小于等于给定的值 | DecimalMax(value=,inclusive=) 小于等于value,inclusive=true表示小于等于 |
@DecimalMin | 所注解的元素必须是数字,且值大于等于给定的值 | |
@Digits | 所注解的元素必须是数字,且值必须是指定的位数 | |
@Future | 所注解的元素必须是将来某个日期 | |
@Max | 所注解的元素必须是数字,且值小于等于给定的值 | Max(value=) |
@Min | 所注解的元素必须是数字,且值小于等于给定的值 | |
@Range | 所注解的元素需在指定范围区间内 | @Range(min = 1, max = 5, message = "") |
@NotNull | 所注解的元素值不能为null | |
@NotBlank | 所注解的元素值有内容 | 常用于String |
@NotEmpty | 不能为null,不能为空字符串"" | 本质是CharSequence, Collection, Map, or Array的size或者length不能为0 |
@Null | 所注解的元素值为null | |
@Past | 所注解的元素必须是某个过去的日期 | |
@PastOrPresent | 所注解的元素必须是过去某个或现在日期 | |
@Pattern | 所注解的元素必须满足给定的正则表达式 | Pattern(regex=,flag=) |
@Size | 所注解的对象必须是Array,Collection,Map,String,且长度大小需保证在给定范围之内 | @Size (min=0, max=1,message="") |
@Length | 验证字符串长度是否在给定的范围之内 | @Length(min=0, max=1,message=“”) |
所注解的元素需满足Email格式 | ||
@DateTimeFormat | 日期格式化注解,将字符串转换为日期格式进行接收 | |
@NumberFormat | number格式化,将字符串转换为数字 |
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NumberFormat(pattern="#,###")
private Integer salary;
@Validated
@RestController
@Validated
public class PingController {
@GetMapping("/getUser")
public String getUserStr(@NotNull(message = "name 不能为空") String name,
@Max(value = 99, message = "不能大于99岁") Integer age) {
return "name: " + name + " ,age:" + age;
}
}
当处理post请求或者请求参数较多的时候使用一个bean来接收参数,然后在每个需要校验的属性上使用参数校验注解。
@Data
public class UserInfo {
@NotNull(message = "username cannot be null")
private String name;
@NotNull(message = "sex cannot be null")
private String sex;
@Max(value = 99L)
private Integer age;
}
@PostMapping("/getUser")
public String getUserStr(@RequestBody@Valid UserInfo user) {
}
用同一个实体类去接收多个controller的参数,但是不同controller所需要的参数又有些许不同。比如有一个/setUser接口不需要id参数,而/getUser接口又需要该参数,这种时候就可以使用参数分组来实现。
public interface GroupA {
}
@RestController
public class PingController {
//其中Default为javax.validation.groups中的类,表示参数类中其他没有分组的参数,如果没有,/getUser接口的参数校验就只会有标记了GroupA的参数校验生效。
@PostMapping("/getUser")
public String getUserStr(@RequestBody @Validated({GroupA.class, Default.class}) UserInfo user, BindingResult bindingResult) {
validData(bindingResult);
return "name: " + user.getName() + ", age:" + user.getAge();
}
@PostMapping("/setUser")
public String setUser(@RequestBody @Validated UserInfo user, BindingResult bindingResult) {
validData(bindingResult);
return "name: " + user.getName() + ", age:" + user.getAge();
}
@Data
public class UserInfo {
@NotNull( groups = {GroupA.class}, message = "id cannot be null")
private Integer id;
@NotNull(message = "username cannot be null")
private String name;
@NotNull(message = "sex cannot be null")
private String sex;
@Max(value = 99L)
private Integer age;
}
当参数bean中的属性又是一个复杂数据类型或者是一个集合的时候,如果需要对其进行进一步的校验.
@Data
public class UserInfo {
@NotNull( groups = {GroupA.class}, message = "id cannot be null")
private Integer id;
@NotNull(message = "username cannot be null")
private String name;
//对UserInfo进一步校验
//这里只能使用@Valid,不能用 @Validated。但valid不支持分组校验,想要支持分组校验可以使用自定义参数校验。
@NotEmpty
private List<@NotNull @Valid UserInfo> parents;
}
虽然JSR303和Hibernate Validtor 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要自定义校验注解。
举例:自定义一个List数组中不能含有null元素的注解。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented //此处指定了注解的实现类为ListNotHasNullValidatorImpl @Constraint(validatedBy = ListNotHasNullValidatorImpl.class) public @interface ListNotHasNull { /** * 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义 */ int value() default 0; String message() default "List集合中不能含有null元素"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; /** * 定义List,为了让Bean的一个属性上可以添加多套规则 */ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) @Retention(RUNTIME) @Documented @interface List { ListNotHasNull[] value(); } }
import org.springframework.stereotype.Service; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.List; public class ListNotHasNullValidatorImpl implements ConstraintValidator<ListNotHasNull, List> { private int value; @Override public void initialize(ListNotHasNull constraintAnnotation) { //传入value 值,可以在校验中使用 this.value = constraintAnnotation.value(); } public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) { for (Object object : list) { if (object == null) { //如果List集合中含有Null元素,校验失败 return false; } } return true; } }
@NotEmpty
@ListNotHasNull
private List<@Valid UserInfo> parents;
如果有很多使用这种参数验证的controller方法,我们希望在一个地方对ConstraintViolationException异常进行统一处理,可以使用统一异常捕获,这需要借助@ControllerAdvice注解来实现,当然在springboot中我们就用@RestControllerAdvice(内部包含@ControllerAdvice和@ResponseBody的特性)
import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; import java.util.Set; /** * @author pengchengbai * @date 2019-06-01 14:09 */ @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ValidationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String handle(ValidationException exception) { if(exception instanceof ConstraintViolationException){ ConstraintViolationException exs = (ConstraintViolationException) exception; Set<ConstraintViolation<?>> violations = exs.getConstraintViolations(); for (ConstraintViolation<?> item : violations) { //打印验证不通过的信息 System.out.println(item.getMessage()); } } return "bad request" ; } }
当参数校验异常的时候,该统一异常处理类在控制台打印信息的同时把bad request的字符串和HttpStatus.BAD_REQUEST所表示的状态码400返回给调用方(用@ResponseBody注解实现,表示该方法的返回结果直接写入HTTP response body 中)。其中:
@ControllerAdvice:控制器增强,使@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的 @RequestMapping注解的方法。
@ExceptionHandler:异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法,此例中处理ValidationException异常。
import org.hibernate.validator.HibernateValidator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; @Configuration public class ValidatorConf { @Bean public Validator validator() { ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure() .failFast( true ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator(); return validator; } }
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = {CheckValueInList.class})
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface ValueInList {
String message() default "(查询字段不支持该值)Query value not be supported";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] values() default {};
}
controller接口参数添加注解,表示值必须是列表中的内容。
@ValueInList(values = {"name", "-id","age" })
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。