引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
定义枚举类存放常亮
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*
*
*/
public enum BizCodeEnume {
//1.提供枚举类的对象,多个对象之间用逗号,最后一个用分号结尾
//相当于 private static finale BizCodeEnume UNKNOW_EXCEPTION = new BizCodeEnume(10000,"系统未知异常")
//就是简化的创建对象
UNKNOW_EXCEPTION(10000,"系统未知异常"),
VAILD_EXCEPTION(10001,"参数格式校验失败");
private int code;
private String msg;
BizCodeEnume(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
1.给Bean添加校验注解
都在:javax.validation.constraints.NotBlank;包下
@NotBlank(message = “品牌名不能为空”)
@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地址
*/
@NotEmpty
@URL(message = "logo必须是合法的url地址")
private String logo;
/**
* 检索首字母
* 自定义校验,可以传正则表达式regexp
*/
@NotEmpty
@Pattern(regexp ="/^[a-zA-Z]/$",message = "首字母必须是a-z或大写")
private String firstLetter;
/**
* 排序
*/
@NotNull
@Min(value = 0,message = "排序必须大于0")
private Integer sort;
}
2.controller @Valid 开启校验
给校验的Bean后面紧跟一个BindingResult,就可以获得校验结果
2.1返回给前台自定义数据格式
1).方法一Controller直接处理
/**
* 保存
*/
//@Valid 开启校验
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result) {
if (result.hasErrors()) {
//封装返回的错误信息
Map<String, String> map = new HashMap<>();
//获取校验错误结果
result.getFieldErrors().forEach(item -> {
//getDefaultMessage() 获取错误提示
String message = item.getDefaultMessage();
//item.getField()获取错误属性名
String fieldName = item.getField();
map.put(fieldName, message);
});
return R.error(400, "数据不合法").put("data", map);
} else {
brandService.save(brand);
return R.ok();
}
}
2). 方法二定义统一异常处理
/**
* 保存
*/
//@Valid 开启校验
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}
如果出现异常会,统一到自定义的异常处理器,处理对应的异常
/**
* @author
* @create 2020-08-22 16:31
* 集中处理所有异常
*/
//处理com.yzh.shop.product.controller包下的所有异常
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.yzh.shop.product.controller")
@RestControllerAdvice(basePackages = "com.yzh.shop.product.controller")
public class ShopExceptionAdvice {
//精确匹配异常
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handlerValidationException(MethodArgumentNotValidException e) {
log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
BindingResult result = e.getBindingResult();
Map<String, String> map = new HashMap<>();
result.getFieldErrors().forEach(item->{
map.put(item.getField(), item.getDefaultMessage());
});
return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data", map);
}
//如果没有精确到异常,就匹配其他任意异常
@ExceptionHandler(value = Throwable.class)
public R handlerException(Throwable throwable) {
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}
二.分组校验
例如 : 添加的时候id是自增 可以为空,修改的时候id 不能是空
1.定义校验接口分组
2.实体类
给校验注解标注什么情况情况需要校验,如果字段不加 不会生效
默认没有指定分组校验的的@NotBlank()在分组情况下不会生效,只会在@Validated生效
@NotBlank(message = "品牌名不能为空",groups = {AddGroup.class,UpdateGroup.class})
package com.yzh.shop.product.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import com.yzh.common.valid.AddGroup;
import com.yzh.common.valid.ListValue;
import com.yzh.common.valid.UpdateGroup;
import com.yzh.common.valid.UpdateStatusGroup;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.*;
/**
* 品牌
*
* @author yzh
* @email yzh@gmail.com
* @date 2020-08-02 12:37:27
*/
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@NotNull(message = "修改必须指定Id" ,groups = UpdateGroup.class)
@Null(message = "新增不能指定id",groups = AddGroup.class)
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名不能为空",groups = {AddGroup.class,UpdateGroup.class})
private String name;
/**
* 品牌logo地址
* 添加组,添加的时候不能为空
*/
@NotBlank(groups = AddGroup.class)
@URL(message = "logo必须是合法的url地址",groups = {AddGroup.class,UpdateGroup.class})
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
@ListValue(vals = {0,1},groups = {AddGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
/**
* 检索首字母
* 自定义校验,可以传正则表达式regexp
*/
@NotEmpty(groups = AddGroup.class)
@Pattern(regexp ="^[a-zA-Z]$",message = "首字母必须是a-z或大写",groups = {AddGroup.class,UpdateGroup.class})
private String firstLetter;
/**
* 排序
*/
@NotNull(groups = AddGroup.class)
@Min(value = 0,message = "排序必须大于0",groups = {AddGroup.class,UpdateGroup.class})
private Integer sort;
}
}
3.Controller用@Validated(UpdateGroup.class)指定校验那一组
/**
* 保存
*/
//@Valid 开启校验
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand) {
brandService.save(brand);
return R.ok();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("product:brand:update")
public R update(@Validated(UpdateGroup.class)@RequestBody BrandEntity brand) {
brandService.updateById(brand);
return R.ok();
}
三.自定义校验
引入最上面的依赖可以不应再次引用下面的依赖
依赖maven
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
1. 编写一个自定义校验的注解
package com.yzh.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;
/**
* @author
*/
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})//关联自定义校验器
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {
//默认错误信息,从哪里读取
String message() default "{com.yzh.common.valid.ListValue.message}";
//支持分组校验功能
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
//默认没有值
int[] vals() default {};
}
2. 编写一个自定义的校验器,ConstraintValidator
package com.yzh.common.valid;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
/**
* @author
* ListValue:校验注解
* Integer:标记在哪个字段
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<Integer>();
//初始化方法
@Override
public void initialize(ListValue constraintAnnotation) {
//我们传的值value是否满足vals的范围
int[] vals = constraintAnnotation.vals();
//判断传来的参数值是否满足vals
for (int val : vals) {
set.add(val);
}
}
//判断是否校验成功
/**
*
* @param value 从页面提交上来,需要校验的值
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
//set集合中是否包含传入的参数值
return set.contains(value);
}
}
3. 关联自定义校验注解和自定义校验器,
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})//关联自定义校验器,可以指定多个不同的,校验器,适配不同类型的校验
@Target({METHOD, FIELD,ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
4.实体类
/**
* 显示状态[0-不显示;1-显示]
*/
@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
@ListValue(vals = {0,1},groups = {AddGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
5.使用读取默认文件内的错误信息,也可以直接在注解用message
5.1在自定义注解中配置
//默认错误信息,从哪里读取
String message() default "{com.yzh.common.valid.ListValue.message}";
5.2在resources下新建
ValidationMessages.properties,文件
com.atguigu.common.valid.ListValue.message=必须提交指定的值
6.controller
/**
* 修改状态
*/
@RequestMapping("/update/status")
//@RequiresPermissions("product:brand:update")
public R updateStatus(@Validated(UpdateStatusGroup.class)@RequestBody BrandEntity brand) {
brandService.updateById(brand);
return R.ok();
}