繁琐校验
当一个请求 使用if else校验时
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@RequestBody BrandEntity brand){
if (!Objects.isNull(brand.getBrandId())){
throw new RuntimeException("新增时不允许id有值");
}else if(StringUtils.isEmpty(brand.getName())){
throw new RuntimeException("新增时name不允许为空");
}else if(....){
....
}else if(....){
....
}else if(....){
....
}
brandService.save(brand);
return R.ok();
}
虽然使用这种看起来没有什么问题,但是如果这个对象有一百个添加接口,就很麻烦。
使用JSR303校验
JSR303是Java为Bean数据合法性校验提供给的标准框架,已经包含在 JavaEE6.0中,JSR303通过在Bean 属性中标注类似 @NotNull @Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean进行验证。
普通校验
注解名称 | 功能 |
---|---|
NotEmpty | 字段不能为" "和null |
URL | 字段必须为http或https格式和null |
Pattern | 自定义匹配规则(正则表达式)和null |
Min | 最小 |
Max | 最大 |
代码
package com.atguigu.gulimall.product.entity;
import com.atguigu.common.exception.GroupInterface.InsertGroup;
import com.atguigu.common.exception.GroupInterface.UpdateGroup;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.*;
/**
* 品牌
*
* @author yangxingchen
* @email yyyxcwl@163.com
* @date 2023-10-07 23:19:45
*/
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
@JsonSerialize(using = ToStringSerializer.class)
private Long brandId;
/**
* 品牌名
*/
@NotEmpty(message = "品牌名不允许为空字符串和null")
private String name;
/**
* 品牌logo地址
*/
@URL(message = "匹配logo地址不允许为空")
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Boolean showStatus;
/**
* 检索首字母
*/
//Pattern是自定义规则,可以使用正则表达式
@Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须是a-z或者A-Z其中一个")
private String firstLetter;
/**
* 排序
*/
@Min(value = 0,message = "排序必须是从0开始得一个整数")
private Integer sort;
}
然后在需要校验接口参数里面加上@Valid
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
自定义拦截器,用来统一返回异常结果
package com.atguigu.gulimall.product.exception;
import com.atguigu.common.exception.HttpCode;
import com.atguigu.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
/*
自定义异常类
*/
@Slf4j
@RestControllerAdvice
public class GulimallExceptionControllerAdvice {
//自定义实体类字段校验拦截器 (MethodArgumentNotValidException是自定义字段异常类)
@ExceptionHandler(value = org.springframework.web.bind.MethodArgumentNotValidException.class)
public R handleVaildException(org.springframework.web.bind.MethodArgumentNotValidException e){
HashMap<String, String> errMap = new HashMap<>();
//取方法字段校验有问题得字段集合,一个一个把他们放到map中,返回给前端
BindingResult bindingResult = e.getBindingResult();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errMap.put(fieldError.getField(),fieldError.getDefaultMessage());
}
return R.error(HttpCode.FIELD_NONCOMPLIANCE.getCode(),HttpCode.FIELD_NONCOMPLIANCE.getMessage()).put("data",errMap);
}
//没有拦截得全部处理
@ExceptionHandler(value = Exception.class)
public R handleException(Exception e){
log.error(e.getMessage(),e.getClass());
return R.error(HttpCode.UNKNOW_EXCEPTION.getCode(),HttpCode.UNKNOW_EXCEPTION.getMessage());
}
}
测试
分组校验
分组校验和普通校验最大得区别就是,可以通过不同接口做不同得校验规则,比如:添加数据接口和修改数据接口
– 添加数据:不需要指定主键ID,使用数据库自增
– 修改数据:必须要携带主键ID,不然修改不了数据
使用分组校验时需要给注解指定group属性一个分组标识符(一个空Interface接口当标识符)
- 创建两个空得interface接口当做新增和修改得标识符
- 在需要做分组字段上使用校验注解并且指定group属性一个分组标识
- 在接口入参添加@Validated(分组标识符),用来表示这个接口是要根据那个分组做校验
代码
- 两个分组空接口标识符
package com.atguigu.common.exception.GroupInterface;
public interface DeleteGroup {
}
package com.atguigu.common.exception.GroupInterface;
public interface InsertGroup {
}
- 实体类表示校验规则和分组标识符
package com.atguigu.gulimall.product.entity;
import com.atguigu.common.exception.GroupInterface.InsertGroup;
import com.atguigu.common.exception.GroupInterface.UpdateGroup;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.*;
/**
* 品牌
*
* @author yangxingchen
* @email yyyxcwl@163.com
* @date 2023-10-07 23:19:45
*/
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
@JsonSerialize(using = ToStringSerializer.class)
//groups 表示controller上表示那个规则到这个里面就根据那个规则校验
@Null(message = "新增时品牌ID必须为空",groups = {InsertGroup.class})
@NotNull(message = "修改时品牌ID不能为空",groups = {UpdateGroup.class})
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名name不允许为空和null",groups = {InsertGroup.class,UpdateGroup.class})
private String name;
/**
* 品牌logo地址
*/
@NotNull(message = "修改时logo不能为空",groups = {InsertGroup.class,UpdateGroup.class})
@URL(message = "这个logo必须是一个合法的url",groups = {InsertGroup.class,UpdateGroup.class})
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Boolean showStatus;
/**
* 检索首字母
*/
@NotEmpty
@Pattern(regexp = "^[a-zA-Z]$",message = "检索首字母必须是a-z字母",groups = {InsertGroup.class,UpdateGroup.class})
private String firstLetter;
/**
* 排序
*/
@Min(value = 0,message = "排序必须大于0")
private Integer sort;
}
- controller接口指定这个方法根据那个分组规则进行校验参数
/**
* 保存
*/
@RequestMapping("/save")
//根据InsertGroup.class进行校验分组规则
public R save(@Validated(InsertGroup.class) @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
/**
* 修改
*/
@RequestMapping("/update")
//根据UpdateGroup.class进行校验分组规则
public R update(@Validated(UpdateGroup.class)@RequestBody BrandEntity brand){
brandService.updateById(brand);
return R.ok();
}
自定义拦截器,用来统一返回异常结果
package com.atguigu.gulimall.product.exception;
import com.atguigu.common.exception.HttpCode;
import com.atguigu.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
/*
自定义异常类
*/
@Slf4j
@RestControllerAdvice
public class GulimallExceptionControllerAdvice {
//自定义实体类字段校验拦截器 (MethodArgumentNotValidException是自定义字段异常类)
@ExceptionHandler(value = org.springframework.web.bind.MethodArgumentNotValidException.class)
public R handleVaildException(org.springframework.web.bind.MethodArgumentNotValidException e){
HashMap<String, String> errMap = new HashMap<>();
//取方法字段校验有问题得字段集合,一个一个把他们放到map中,返回给前端
BindingResult bindingResult = e.getBindingResult();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errMap.put(fieldError.getField(),fieldError.getDefaultMessage());
}
return R.error(HttpCode.FIELD_NONCOMPLIANCE.getCode(),HttpCode.FIELD_NONCOMPLIANCE.getMessage()).put("data",errMap);
}
//没有拦截得全部处理
@ExceptionHandler(value = Exception.class)
public R handleException(Exception e){
log.error(e.getMessage(),e.getClass());
return R.error(HttpCode.UNKNOW_EXCEPTION.getCode(),HttpCode.UNKNOW_EXCEPTION.getMessage());
}
}