springBoot参数校验

有参数传递的地方都少不了参数校验。在web开发中,前端的参数校验是为了用户体验,后端的参数校验是为了安全。试想一下,如果在controller层中没有经过任何校验的参数通过service层、dao层一路来到了数据库就可能导致严重的后果,最好的结果是查不出数据,严重一点就是报错,如果这些没有被校验的参数中包含了恶意代码,那就可能导致更严重的后果。

这里我们主要介绍在springboot中的几种参数校验方式。常用的用于参数校验的注解如下:

  • @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格式

其他 Difference Between @NotNull, @NotEmpty, and @NotBlank

  • @NotNull 不能为null,但是可以为空字符串"" 一般用于Date,int类型

  • @NotEmpty 不能为null,不能为空字符串"",其本质是CharSequence, Collection, Map, or Array的size或者length不能为0 用于String类型

  • @NotBlank a constrained String is valid as long as it’s not null and the trimmed length is greater than zero

  • @NonNull

  • @NotNull 是 JSR303(Bean的校验框架)的注解,用于运行时检查一个属性是否为空,如果为空则不合法。

  • @NonNull 是JSR 305(缺陷检查框架)的注解,是告诉编译器这个域不可能为空,当代码检查有空值时会给出一个风险警告,目前这个注解只有IDEA支持。

controller.service层参数校验

在controller层的参数校验可以分为两种场景:

  1. 单个参数校验
  2. 实体类参数校验

单个参数校验

@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;
    }
}

注意单参数使用时,注解要加到类上才可生效

实体类参数校验

public class User {
 
	/** id */
	@NotNull(message="id不能为空")
	private Long id;
	
	/** 姓名 */
	@NotBlank(message="姓名不能为空")
	private String name;
	
	/** 年龄 */
	@NotNull(message="年龄不能为空")
	@Max(message="年龄不能超过120岁", value = 120)
	@Min(message="年龄不能小于1岁", value = 1)
	private Integer age;
	
	/** 创建时间 */
	@Future
	private Date createTime;
}

参数前面加@Validated注解

@RestController
@Validated
public class PingController {

    @GetMapping("/getUser")
    public String getUserStr(@Validated String name,@Validated Integer age) {
        return "name: " + name + " ,age:" + age;
    }
}

@Valid注解的三种校验方式

  • 在Controller方法参数前加@Valid注解——校验不通过时直接抛异常
@PostMapping("/test1")
public Object test1(@Valid User user) {
	return "操作成功!";
}
  • 在Controller方法参数前加@Valid注解,参数后面定义一个BindingResult类型参数——执行时会将校验结果放进bindingResult里面,用户自行判断并处理
@PostMapping("/test2")
public Object test2(@RequestBody @Valid User user, BindingResult bindingResult) {
	// 参数校验  
    //使用异常处理类来进行处理异常
    Validator.validData(bindingResult);
            
	if (bindingResult.hasErrors()) {
		String messages = bindingResult.getAllErrors()
		.stream()
		.map(ObjectError::getDefaultMessage)
		.reduce((m1, m2) -> m1 + ";" + m2)
		.orElse("参数输入有误!");
		throw new IllegalArgumentException(messages);
	}
	
	return "操作成功!";
}
 

注意:如果有多个参数, BindingResult bindingResult1,BindingResult bindingResult2, 命名不同

  • 用户手动调用对应API执行校验——Validation.buildDefault ValidatorFactory().getValidator().validate(xxx)
/**
	 * 用户手动调用对应API执行校验
	 * @param user
	 * @return
	 */
	@PostMapping("/test3")
	public Object test3(@RequestBody User user) {
		// 参数校验
		validate(user);
		
		return "操作成功!";
	}
 
	private void validate(@Valid User user) {
		Set<ConstraintViolation<@Valid User>> validateSet = Validation.buildDefaultValidatorFactory()
				.getValidator()
				.validate(user, new Class[0]);
			if (!CollectionUtils.isEmpty(validateSet)) {
				String messages = validateSet.stream()
					.map(ConstraintViolation::getMessage)
					.reduce((m1, m2) -> m1 + ";" + m2)
					.orElse("参数输入有误!");
				throw new IllegalArgumentException(messages);
				
			}
	}

异常处理类

public class Validator{
    public static void validData(BindingResult bindingResult){
        if(bindingResult.hasErrors() ){
            for(ObjectError error:bindingResult.getAllErrors() ){
                throw new ManageException(error.getDefaultMessage(),ManageCOnstants.SERVERERROR);
            }
        }
    }
}

参数校验分组

在实际开发中经常会遇到这种情况:想要用一个实体类去接收多个controller的参数,但是不同controller所需要的参数又有些许不同,而你又不想为这点不同去建个新的类接收参数。比如有一个/setUser接口不需要id参数,而/getUser接口又需要该参数,这种时候就可以使用参数分组来现。

  1. 定义表示组别的interface;
public interface GroupA {
//此处就是空接口
}
  1. 在实体类的注解中标记这个哪个组所使用的参数;

//一个实体类中可以定义多个组接口
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;
}
  1. 在@Validated中指定使用哪个组;
//其中Default为javax.validation.groups中的类,表示参数类中其他没有分组的参数,如果没有,/getUser接口的参数校验就只会有标记了GroupA的参数校验生效。
//但是Default我不知道有什么用,加不加无所谓
@RestController
public class PingController {
    @PostMapping("/getUser")
    public String getUserStr(@Validated({GroupA.class, Default.class}) UserInfo user, BindingResult bindingResult) {
        validData(bindingResult);
        return "name: " + user.getName() + ", age:" + user.getAge();
    }

    @PostMapping("/setUser")
    public String setUser(@Validated UserInfo user, BindingResult bindingResult) {
        validData(bindingResult);
        return "name: " + user.getName() + ", age:" + user.getAge();
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值