前端校验
element-ui 官方文档
https://element.eleme.cn/#/zh-CN/component/form
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="检索首字母" prop="firstLetter">
<el-input v-model="dataForm.firstLetter" placeholder="检索首字母"></el-input>
</el-form-item>
</el-form>
<script>
export default {
data () {
var validate1 = (rule, value, callback) => {
if (value === '') {
callback(new Error("首字母不能为空"))
}else if(!/^[a-zA-Z]$/.test(value)){
callback(new Error("首字母必须是a-z或A-Z"))
}else {
callback();
}
};
return {
dataForm: {
firstLetter: ''
},
dataRule: {
name: [
firstLetter: [
{ validator: validate1, trigger: 'blur' }
]
]
}
}
}
</script>
后端校验 JSR303
前端校验目的:优化用户的感受
后端检验目的:防止越过前端,直接使用错误数据访问后端,例如用 postman
1、依赖
<!-- JSR303校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.4.5</version>
</dependency>
2、注解
注解 | 描述 |
---|---|
@Pattern | 自定义校验,参数为正则表达式 |
@Positive | 正数 |
@PositiveOrZero | 0和正数 |
@Negative | 负数 |
@NegativeOrZero | 0和负数 |
@NotBlank | 非空,用于字符序列(一个以上非空字符) |
@NotEmpty | 非空,用于字符序列(一个以上非空字符)、集合、map、数组 |
@NotNull | 不为null,用于所有类型 |
@Size(max = 20,min = 0) | 长度,用于字符序列(一个以上非空字符)、集合、map、数组 |
@Length | 同上,不过只能用于字符串 |
@URL | 校验是否为url,用于String |
3、实体类定义校验规则
使用 javax.validation.constraints
包下的检验注解
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
* 表单验证不为空
*/
@NotBlank(message = "品牌名不能为空")
private String name;
/**
* 品牌logo地址
* 表单验证必须为 url 地址
*/
@URL(message = "品牌的logo地址格式错误")
private String logo;
/**
* 介绍
*/
@NotBlank(message = "品牌的介绍不能为空")
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@TableLogic
private Integer showStatus;
/**
* 检索首字母
* 校验首字母必须是 a-z或者A-Z
*/
@Pattern(regexp="^[a-zA-Z]$",message = "检索首字母必须是一个字母")
private String firstLetter;
/**
* 排序
* 校验不为空 并且值大于0
*/
@NotNull
@Min(value = 0,message = "排序必须大于等于0")
private Integer sort;
}
4.1、局部校验方法
@Valid
: 开启校验功能,效果:校验错误以后会有默认的响应;
BindingResult
给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果
/**
* 保存
*/
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
if (result.hasErrors()){
Map<String,String> map = new HashMap<>();
//1、获取校验的错误结果
result.getFieldErrors().forEach((item)->{
//FieldError 获取到错误提示
String message = item.getDefaultMessage();
//获取错误的属性的名字
String field = item.getField();
map.put(field,message);
});
return R.error(400,"提交的数据不合法").put("data",map);
}
brandService.save(brand);
return R.ok();
}
4.2、全局校验
@PostMapping("/saveUser2")
public R saveUser2(@Valid @RequestBody User user){
return R.ok().put("data",user);
}
/**
* 全局异常处理
*/
@Slf4j
@RestControllerAdvice(basePackages = "com.rewind.jsr303.controller")
public class MyExceptionHandler {
/**
* 数据校验异常捕获
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R validException(MethodArgumentNotValidException e){
log.error("数据校验出现问题:{},异常类型:{}",e.getMessage(),e.getClass());
// 校验错误信息
BindingResult result = e.getBindingResult();
HashMap<String, String> map = new HashMap<>();
// 获取出现异常的字段
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors) {
map.put(error.getField(),error.getDefaultMessage());
}
return R.error(400,"数据校验出现异常").put("data",map);
}
/**
* 所有异常捕获
* @param e
*/
@ExceptionHandler
public void globalException(Exception e){
log.error("全局异常{}",e.getMessage());
}
}
4.3、分组校验
场景:新增时,id 字段必须为空,而修改时,id 字段必须不为空。
(1)创建分组接口
/**
* 新增校验分组
*/
public interface AddGroup {
}
/**
* 修改校验分组
*/
public interface UpdateGroup {
}
(2)定义分组规则
@Data
public class User {
// 分组为新增时才执行校验
@Null(message = "新增时主键必须为空",groups = {AddGroup.class})
// 分组为修改时才执行校验
@NotNull(message = "修改时主键不能为空",groups = {UpdateGroup.class})
private Long id;
}
(3)分组
@Validated
: 是Spring提供的,使用该注解后,没加分组的校验注解将不起作用
@PostMapping("/saveUser3")
public R saveUser3(@Validated(AddGroup.class) @RequestBody User user){
return R.ok().put("data",user);
}
@PostMapping("/updateUser")
public R updateUser(@Validated(UpdateGroup.class) @RequestBody User user){
return R.ok().put("data",user);
}
(4)异常处理
同4.2全局校验
5、自定义校验注解
案例:该自定义注解校验对象只能是指定的一个或多个值
(1)自定义注解
/**
* 自定义校验注解
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
// 指定自定义校验器
// 数组,可指定多个校验器,即当需要该注解对多种类型的字段生效时,可指定多个校验器
// 会自动使用类型适配的校验器进行校验
@Constraint(validatedBy = { ListValueConstraintValidator.class })
public @interface ListValue {
// 指定默认的message信息,当前自定义注解的全包名+message
// 在ValidationMessages.properties中定义
String message() default "{com.rewind.jsr303.valid.ListValue.message}";
// 可直接指定字符串
//String message() default "必须是指定的值哦!!!";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
// 注解的自定义属性
int[] values() default {};
}
(2)自定义校验器
/**
* 自定义校验器
* 泛型1:自定义注解
* 泛型2:自定义注解可使用的字段类型
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private HashSet set = new HashSet();
/**
* 初始化方法
*/
@Override
public void initialize(ListValue constraintAnnotation) {
// 获取自定义注解的详细参数信息,参数values的值
int[] values = constraintAnnotation.values();
for (int value : values) {
set.add(value);
}
}
/**
* 判断是否校验成功
* @param value 被校验字段的值
* @param context 校验的上下文环境信息
* @return 是否校验成功
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
// 返回 set是否包含被校验字段
return set.contains(value);
}
}
(3)message配置文件
在resource目录下创建 ValidationMessages.properties
配置文件,编码为utf8
用于指定默认的报错信息
com.rewind.jsr303.valid.ListValue.message = Must be the specified value
(4)使用注解
@Data
public class User {
// 自定义校验注解
@ListValue(values = {0,1,2},groups = AddGroup.class)
private Integer status;
}