参数校验对于我们Web开发是非常重要的。第一,作为服务端的开发者,如果你的参数校验写的足够规范是可以大大提高前后端协同开发的开发效率的,从而为公司及自己大大节约时间成本以及经济成本。第二,参数校验对于保护Web里面的机密数据和机要信息也是非常重要的。校验的代码不能直接写在控制器里。控制器主要是用于承接视图层与服务层之间的桥梁,不是用来编写主要的业务逻辑的,也不是用来写大量的校验的代码的。本文要介绍的参数校验为注解校验的方式。
参数接收
我们要去做参数校验首先要学习的是如何在控制器里能够方便的接收到参数。参数主要分两大类,一类是通过url传递过来的参数,另一类是通过post的body里传递过来的参数。
获取URL路径中的参数和查询参数
url传递的参数也分两种,一种是在路径里的参数,如下面代码中的id1
。一种是查询参数,也就是?后面的参数,如下面代码中的name2
。路径里的参数通过注解@PathVariable
来接收,查询参数可以不用注解,也可以通过加@RequestParam
来接收。当参数名与方法中接收参数的参数名不一致时,可以通过在注解后面加name参数来映射。
@GetMapping(value = "/test/{id1}")
public String test(@PathVariable(name="id1") Integer id,@RequestParam(name="name2") String name){
发送请求,请求url为 http://localhost:8080/v1/banner/test/2?name2=雪,查看接收到的参数。
数据传输对象DTO的接收
当我们要传递大量数据的时候,通常采用POST请求在httpbody中传JSON格式的数据
接收这种JSON格式的数据用注解@RequestBody 。接收的数据类型可以定义成一个Map<String,Object>,但是这种方式接收到的参数Object在使用时还需要转型,频繁的拆箱装箱对性能是有一定影响的。我们通常定义一个类来接收参数。
@PostMapping(value = "/test/{id1}")
public PersonDTO test(@PathVariable(name="id1") Integer id,
@RequestParam(name="name2") String name,
@RequestBody Map<String,Object> personDTO){
定义类接收对象
@Getter
@Setter
public class PersonDTO {
private String name;
private Integer age;
}
@PostMapping(value = "/test/{id1}")
public PersonDTO test(@PathVariable(name="id1") Integer id,
@RequestParam(name="name2") String name,
@RequestBody PersonDTO personDTO){
参数校验
基础参数校验
常见注解
Bean validation中内置的constraint
@Null 被注释的元素必须为null
@NotNull 被注释的元素必须不为null
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value=) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value=) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Positive 被注释的元素必须为正数,即数值大于 0
@PositiveOrZero 被注释的元素必须为正数或 0,即数值大于等于 0
@DecimalMin(value=,inclusive=) 被注释的元素必须是一个数字,其值必须大于等于value,inclusive=true,是大于等于
@DecimalMax(value=,inclusive=) 被注释的元素必须是一个数字,其值必须小于等于value,inclusive=true,是小于等于
@Size(min=, max=) 字符串,集合,map 被注释的元素的大小必须在指定的范围内
@Digits(integer,fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate validator附加的constraint
@NotBlank(message=) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
基本使用方式
要想开启参数校验,需要在类上标注@Validated注解
@Validated
public class BannerController {
@PostMapping(value = "/test/{id1}")
public PersonDTO test(@PathVariable(name="id1") @Range(min = 1,max = 10,message = "不能超过10噢") Integer id){
验证HTTP Body中的参数与级联校验
如果要开启Http Body中的参数校验,那么在参数列表该字段前加上@Validated即可
@Validated
public class BannerController {
@PostMapping(value = "/test")
public PersonDTO test(@RequestBody @Validated PersonDTO personDTO){
如果一个类中包含了另外一个实体类,那么在上面加上@Valid即可
public class PersonDTO {
@Valid
private SchoolDTO schoolDTO;
}
自定义校验注解
当基础的校验注解不能满足我们自己的业务需求时,我们就需要自定义注解了。
1.创建一个注解
@Documented 注解标记的元素,Javadoc工具会将此注解标记元素的注解信息包含在javadoc中。默认,注解信息不会包含在Javadoc中。
@Retention({RetentionPolicy.Runtime}) RetentionPolicy这个枚举类型的常量描述保留注释的各种策略,它们与元注释(@Retention)一起指定注释要保留多长时间
@Target({ElementType.TYPE}) 用于描述注解可以用在什么地方
@Constraint(validatedBy = PasswordValidator.class)将自定义校验注解与关联类关联在一起。validatedBy接收的参数可以是一个数组,也就是可以指定多个关联类来修饰这个自定义注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Constraint(validatedBy = PasswordValidator.class)
public @interface PasswordEqual {
int min() default 2;
int max() default 10;
String message() default "passwords are not equal";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2.创建关联类
自定义校验注解还需要一个关联类,校验的业务逻辑应该写在关联类中。关联类要实现ConstraintValidator
这个接口,这个接口是一个泛型类,第一个参数是注解的类型,第二个参数是这个自定义注解所修饰的目标的类型。重写initialize()方法,在initialize()方法中获取注解的参数。重写isValid()方法校验参数是否通过校验
import com.lin.missyou.dto.PersonDTO;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PasswordValidator implements ConstraintValidator<PasswordEqual, PersonDTO> {
private int min;
private int max;
/**获取注解的参数*/
@Override
public void initialize(PasswordEqual constraintAnnotation) {
this.min = constraintAnnotation.min();
this.max = constraintAnnotation.max();
}
@Override
public boolean isValid(PersonDTO personDTO, ConstraintValidatorContext constraintValidatorContext) {
String password1 = personDTO.getPassword1();
String password2 = personDTO.getPassword2();
boolean match = password1.equals(password2);
return match;
}
}