SpringBoot 表单参数校验

在前后端分离如此盛行的今天,普遍使用 Json 进行数据交互,为了保证数据的完整与有效性,我们一般会在前端提交数据前进行一次数据校验,但这并不能完全保证数据的可靠,部分别有用心的人可能会绕过前端校验,直接通过浏览器控制台(F12)或一些抓包工具(charles/filder)获取我们的借口,并手动拼接一些非法参数。为了避免这种情况的发生,就不得不在后台再进行一次请求数据校验。

一般情况下,我们在后台进行数据校验是这样的:

    @PostMapping("/add")
    public Object addUser1(User user){
        if (user.getUsername() == null || "".equals(user.getUsername())){
            return BaseResultVO.fail("用户名不能为空");
        }
        if (user.getPassword() == null || "".equals(user.getPassword())){
            return BaseResultVO.fail("密码不能为空");
        }
        log.info("登录用户名:[{}] 密码:[{}] 年龄:[{}]",user.getUsername(),user.getPassword(),user.getAge());
        Integer addStatus = userService.addUser(user);
        return BaseResultVO.ok("用户添加成功");
    }

我们可以看到,如果需要校验的字段很多,这样将会产生很多的冗余代码,因此我们需要改进一下。
我们可以用到 Hibernete validator 提供给我们的注解来简化我们的操作,在SpringBoot 中直接引入 spring-boot-starter-web,即可自动帮我们引入Hibernete validato 的jar包

image


实体bean

@Data
@Builder
@Table(name = "dt_user")
@JsonIgnoreProperties(value = {"ifDeleted","password",})
public class User extends BaseModel {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(generator = "JDBC")
    private Integer userId;

    @NotEmpty(message = "用户名不能为空",groups = {LoginValidatedGroup.class,UserAddValidatedGroup.class})
    private String username;

    @NotEmpty(message = "密码不能为空",groups = {LoginValidatedGroup.class, UserAddValidatedGroup.class})
    private String password;

    @NotNull(message = "年龄不能为空",groups = {UserAddValidatedGroup.class})
    private Integer age;

    private String ifDeleted;

    private String openId;

    private String headImg;

    private String roleId;
}

groups 属性可以方便的在各种场景下校验不同的参数
比如,在登录时,用户名密码不能为空,
在添加用户时,用户名密码以及年龄不能为空等等。
我们可以为不同的业务场景定义起不同的group, LoginValidatedGroup.class, UserAddValidatedGroup.class 这些其实就是一个空的接口

/**
 *  
 *  @ClassName: UserAddValidatedGroup 
 *  @author wangwei
 *  @date 2018/10/9 10:45 
 *  @Description: 添加用户参数 校验组
 */
public interface UserAddValidatedGroup {
}

定义好上面这些后,我们再来看看控制层怎么去处理

@PostMapping("/add")
    public Object addUser2(@Validated(value = {UserAddValidatedGroup.class}) User user, BindingResult bindingResult){
        if (bindingResult.hasErrors()){
            String errMsg = bindingResult.getFieldError().getDefaultMessage();
            return BaseResultVO.fail(errMsg);
        }
        log.info("登录用户名:[{}] 密码:[{}] 年龄:[{}]",user.getUsername(),user.getPassword(),user.getAge());
        Integer addStatus = userService.addUser(user);
        return BaseResultVO.ok("用户添加成功");
    }

这样看似还不错,但如果我们每个接口都要去判断 bindingResult.hasErrors(),我们的代码也将变得臃肿。在这里我们可以通过Spring的AOP特性去做一个通用切面,去处理字段校验失败后的逻辑:

/**
 * 采用AOP切面处理参数校验问题
 * @author wangwei
 */
@Component
@Aspect
public class BindingResultAop {

    /**
     * 切入点
     * 设置切入点为web层
     * AspectJ支持命名切入点,方法必须是返回void类型
     */
    @Pointcut("execution(* com.*.*.*.controller..*.*(..))")
    public void aopMethod(){}

    /**
     * 检查 Controller 方法的参数是否有效
     * 环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行其相应逻辑
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("aopMethod()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        BindingResult bindingResult = null;
        for(Object arg:joinPoint.getArgs()){//遍历被通知方法(controller方法)的参数列表
            if(arg instanceof BindingResult){//参数校验结果会存放在BindingResult中
                bindingResult = (BindingResult) arg;
            }
        }
        if(bindingResult != null){
            if(bindingResult.hasErrors()){//检查是否存在校验错误
                String errorInfo = "";
                List errors = bindingResult.getFieldErrors();//获取字段参数不合法的错误集合
                for(FieldError error : errors){
                    errorInfo = errorInfo + "[" + error.getField() + " " + error.getDefaultMessage() + "]";
                }
                return BaseResultVO.builder().message(errorInfo).errorCode("400").build(); //返回异常错误
            }
        }
        return joinPoint.proceed();//执行目标方法
    }
}

支持,我们就不必每个接口去判断bindingResult.hasErrors(),接下来我们控制层Controller的代码就成这样了:

@PostMapping("/add")
    public Object addUser(@Validated(value = {UserAddValidatedGroup.class}) User user, BindingResult bindingResult){
        log.info("登录用户名:[{}] 密码:[{}] 年龄:[{}]",user.getUsername(),user.getPassword(),user.getAge());
        Integer addStatus = userService.addUser(user);
        return BaseResultVO.ok("用户添加成功");
    }

是不是很精简啦
Postman测试一下:


image

image
  • 本文作者: 王维
  • 原文链接: http://blog.foreveronline.top/archives/springboot-biao-dan-can-shu-xiao-yan

你可能感兴趣的:(SpringBoot 表单参数校验)