springboot+vue+element-ui实现前后端的全部校验

1、前端校验

el-form的表单校验

<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
  <el-form-item label="密码" prop="pass">
    <el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input>
  </el-form-item>
  <el-form-item label="确认密码" prop="checkPass">
    <el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input>
  </el-form-item>
  <el-form-item label="年龄" prop="age">
    <el-input v-model.number="ruleForm.age"></el-input>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
    <el-button @click="resetForm('ruleForm')">重置</el-button>
  </el-form-item>
</el-form>
<script>
  export default {
    data() {
      var checkAge = (rule, value, callback) => {
        if (!value) {
          return callback(new Error('年龄不能为空'));
        }
        setTimeout(() => {
          if (!Number.isInteger(value)) {
            callback(new Error('请输入数字值'));
          } else {
            if (value < 18) {
              callback(new Error('必须年满18岁'));
            } else {
              callback();
            }
          }
        }, 1000);
      };
      var validatePass = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请输入密码'));
        } else {
          if (this.ruleForm.checkPass !== '') {
            this.$refs.ruleForm.validateField('checkPass');
          }
          callback();
        }
      };
      var validatePass2 = (rule, value, callback) => {
        if (value === '') {
          callback(new Error('请再次输入密码'));
        } else if (value !== this.ruleForm.pass) {
          callback(new Error('两次输入密码不一致!'));
        } else {
          callback();
        }
      };
      return {
        ruleForm: {
          pass: '',
          checkPass: '',
          age: ''
        },
        rules: {
          pass: [
            { validator: validatePass, trigger: 'blur' }
          ],
          checkPass: [
            { validator: validatePass2, trigger: 'blur' }
          ],
          age: [
            { validator: checkAge, trigger: 'blur' }
          ]
        }
      };
    },
    methods: {
      
    }
  }
</script>

自定义校验器

<el-form
      :model="dataForm"
      :rules="dataRule"
      ref="dataForm"
      @keyup.enter.native="dataFormSubmit()"
      label-width="140px"
    >
</el-form>
data() {
      return {
        visible: false,
        dataForm: {
          brandId: 0,
          name: "",
          logo: "",
          descript: "",
          showStatus: "",
          firstLetter: "",
          sort: "",
        },
        dataRule: {
          name: [{required: true, message: "品牌名不能为空", trigger: "blur"}],
          logo: [
            {required: true, message: "品牌logo地址不能为空", trigger: "blur"},
          ],
          descript: [
            {required: true, message: "介绍不能为空", trigger: "blur"},
          ],
          showStatus: [
            {
              required: true,
              message: "显示状态[0-不显示;1-显示]不能为空",
              trigger: "blur",
            },
          ],
          firstLetter: [
            {
              validator: (rule, value, callback) => {
                if (value == "") {
                  callback(new Error("请输入首字母"));
                } else if (!(/^[a-zA-Z]$/).test(value)) {
                  callback(new Error("请输入英文字母"));
                } else {
                  callback();
                }

              }, trigger: "blur"
            },
          ],
          sort: [{
            validator: (rule, value, callback) => {
              if (value == '') {
                callback(new Error("请输入排序字段"));
              } else if (!Number.isInteger(value) || value < 0) {
                callback(new Error('排序字段必须是一个整数并且大于0'))
              } else {
                callback();
              }
            }, trigger: "blur"
          }],
        },
      };
    },

 

2、JSR303后端校验

后端的校验注解:

代码:

package com.atguigu.gulimall.product.entity;

import com.atguigu.common.valid.AddGroup;
import com.atguigu.common.valid.ListValue;
import com.atguigu.common.valid.UpdateGroup;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.util.Date;

//import com.sun.istack.internal.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.URL;

import javax.validation.constraints.*;

/**
 * 品牌
 * 
 * @author pansd
 * @email 1152777670@qq.com
 * @date 2022-02-23 16:02:02
 */
@Data
@TableName("pms_brand")
public class PmsBrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@TableId
	@Null(message = "新增时不能填写品牌id",groups = {AddGroup.class}) //实现分组检验;
	@NotNull(message = "修改时必须填写品牌id",groups = {UpdateGroup.class})
	private Long brandId;
	/**
	 * 品牌名
	 */
	@NotBlank(message = "品牌名不能为空",groups = {AddGroup.class,UpdateGroup.class}) //不能是一个空格,至少是一个非空字符;如果仅仅在此处标准,默认是不起作用的。得在controller@Valid//响应状态码为400,是校验不通过的。
	private String name;
	/**
	 * 品牌logo地址
	 */
	@URL  //如果没有增加分组的校验,那么它的注解是不起作用的。@Validated({AddGroup.class})
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@ListValue(vals = {0,1})
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	@Pattern(regexp = "^[a-zA-Z]$",message = "检索首字母必须是一个字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	@Min(value = 0)
	@NotNull //sort字段是一个Integer字段,不能使用notEmpty来标注
	private Integer sort;

}

 注意:此时在实体类中增加的校验是不起作用的。需要在controller中配置@Valid才能使用

代码如下:

@RequestMapping("/save2")
    public R save2(@Valid @RequestBody PmsBrandEntity brandEntity){
        pmsBrandService.save(brandEntity);
        return R.ok();
    }

 此时,返回的错误信息较为杂乱,为了方便处理,需要返回统一的异常处理。

3、校验的统一异常处理

针对于某个接口进行特定的处理方式:

    @RequestMapping("/save")
    public R save(@Valid @RequestBody PmsBrandEntity pmsBrand, BindingResult bindingResult){  //获取校验结果的信息,从而对错误信息进行封装
        //在json后边,紧跟着一个校验的结果。BindingResult bindingResult,拿到校验的结果,就可以封装。
        boolean b = bindingResult.hasErrors(); //是否有错误
        if(b){
            Map<String,Object> map = new HashMap<>();
            //获取校验数据的结果
            bindingResult.getFieldErrors().forEach((item)->{
                //获取校验字段的名字
                String field = item.getField();
                //获取校验信息
                String defaultMessage = item.getDefaultMessage();
                map.put(field,defaultMessage);
            });
            return R.error(400,"提交的数据不合法!").put("data",map);
        }
        pmsBrandService.save(pmsBrand);
        return R.ok();
    }

全局进行处理,代码如下:

  @RequestMapping("/save2")
    public R save2(@Valid @RequestBody PmsBrandEntity brandEntity){
        pmsBrandService.save(brandEntity);
        return R.ok();
    }

package com.atguigu.gulimall.product.exception;

import com.atguigu.common.exception.BizCodeEnum;
import com.atguigu.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * @author pshdhx
 * @date 2022-03-04 11:12
 * @Des 统一标准来处理异常的
 */
@Slf4j
//@ControllerAdvice
//@ResponseBody
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class PshdhxGuliControllerAdvice {

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e){
        BindingResult bindingResult = e.getBindingResult();
        Map<String,Object> map = new HashMap<>();
        bindingResult.getFieldErrors().forEach(item->{
            //...

            //获取校验字段的名字
            String field = item.getField();
            //获取校验信息
            String defaultMessage = item.getDefaultMessage();
            map.put(field,defaultMessage);
        });
        log.error("数据校验出现问题{},异常类型{}",e.getMessage(),e.getClass());
        return R.error(BizCodeEnum.VAILD_EXCEPTION.getCode(),BizCodeEnum.VAILD_EXCEPTION.getMsg()).put("data",map);
        //return R.error(BizCodeEnum.VAILD_EXCEPTION.getCode(),BizCodeEnum.VAILD_EXCEPTION.getMsg()).put("data",map);

    }
    
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
        log.error("错误:",throwable);
        return R.error(BizCodeEnum.UNKNOW_EXCEPTION.getCode(),BizCodeEnum.UNKNOW_EXCEPTION.getMsg());
    }
}

为了统一自定义异常编码,指定了枚举类型。

package com.atguigu.common.exception;

/**
 * @author pshdhx
 * @date 2022-03-04 11:25
 */
public enum  BizCodeEnum {
    UNKNOW_EXCEPTION(10000,"系统未知异常"),
    VAILD_EXCEPTION(10001,"参数格式校验失败");

    private int code;
    private String msg;
    BizCodeEnum(int code,String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

 

结果如下: 

 

4、JSR303后端分组检验

比如说id这个字段,在新增时,不需要它;在修改时,必须要它,那么就需要对其进行分组处理

@Data
@TableName("pms_brand")
public class PmsBrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@TableId
	@Null(message = "新增时不能填写品牌id",groups = {AddGroup.class}) //实现分组检验;
	@NotNull(message = "修改时必须填写品牌id",groups = {UpdateGroup.class})
	private Long brandId;
package com.atguigu.common.valid;

public interface AddGroup {
}

 

package com.atguigu.common.valid;

public interface UpdateGroup {
}

定义俩空接口即可。但是,要想使其生效,则必须在控制器中使用注解@Validated

    //实现分组校验,这是添加是要校验的内容
    @RequestMapping("/save4")
    public R save4(@Validated({AddGroup.class}) @RequestBody PmsBrandEntity brandEntity){
        pmsBrandService.save(brandEntity);
        return R.ok();
    }

 但是,如果其他字段没有增加上相关的分组,则字段上的注释会失效。

5、JSR303自定义校验规则

我想让某个字段只输入0和1,需要自定义注解

	@ListValue(vals = {0,1})
	private Integer showStatus;

1、自定义校验规则 

package com.atguigu.common.valid;
/*
自定义校验规则
 */
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class }) //关联
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{com.atguigu.common.valid.ListValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    int[] vals() default { };
}

2、自定义校验器

package com.atguigu.common.valid;
/**
 * 自定义校验器
 */

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {

    private Set<Integer> set = new HashSet<>();
    //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {

        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
            set.add(val);
        }

    }

    //判断是否校验成功

    /**
     *
     * @param value 需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

        return set.contains(value);
    }
}

3、关联校验规则和校验器

@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class }) //关联
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {

4、校验信息定义

ValidationMessages.properties

com.atguigu.common.valid.ListValue.message=必须提交指定的值
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值