JSR303后端校验详细笔记
JSR303
使用步骤
1.依赖
<!--数据校验-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
2.在entity类的属性上添加注解
3.开启校验功能:在controller类的方法的参数上加上@Valid属性
4.校验失败的处理:
- 第一种:单独处理
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);
}else {
brandService.save(brand);
}
return R.ok();
}
- 第二种,抛出异常后统一处理
- 定义
@RestControllerAdvice
处理请求异常类 - 将
@ExceptionHandler(value= xxx.class)
注解根据异常类型标注在方法上,编写处理逻辑
@Slf4j
@RestControllerAdvice
public class ExceptionControllerAdvice {
@ExceptionHandler(value= MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e){
log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
BindingResult bindingResult = e.getBindingResult();
Map<String,String> errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach((error)->{
//存储校验字段名,以及校验字段失败提示
errorMap.put(error.getField(),error.getDefaultMessage());
});
return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
}
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
log.error("错误:",throwable);
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}
- 定义异常枚举类
public enum BizCodeEnume {
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;
}
}
关于不为空
-
@NotNull
只要不为空,校验任意类型
The annotated element must not be {@code null}.
Accepts any type.
-
@NotBlank
至少有一个非空字符,校验字符
The annotated element must not be {@code null} and must contain at least one
non-whitespace character. Accepts {@code CharSequence}.
-
@NotEmpty
非空,也不能内容为空,校验字符,集合,数组
The annotated element must not be {@code null} nor empty. Supported types are:
{@code CharSequence} (length of character sequence is evaluated)
{@code Collection} (collection size is evaluated)
{@code Map} (map size is evaluated)
Array (array length is evaluated)
分组校验
步骤:
-
在校验注解上加上
groups = {xxx.class, ...}
属性,值可以是任意interface接口,例如@URL(message = "logo必须是一个合法的url地址",groups={AddGroup.class,UpdateGroup.class})
; -
在开启校验处,将
@Valid
注解改为@Validated({xxx.class})
,例如@Validated({AddGroup.class})
就表示只校验该组的属性;注意:未添加任何分组的校验将会无效,开启娇艳的时候i如果添加了分组信息,那么只会校验同样页添加了该分组的属性。
自定义校验
1)、编写一个自定义的校验注解
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "{com.lx.common.valid.ListValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default { };
}
2)、编写配置文件ValidationMessages.properties
,给自定义的校验配置校验失败的信息
com.lx.common.valid.ListValue.message=显示状态只能为1或0
3)、编写一个自定义的校验器 ConstraintValidator
实现ConstraintValidator接口,第一个参数为绑定的校验注解名,第二个参数为校验的属性类型,完成初始化与判断方法。
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<>();
/**
* @Description: 根据注解中的属性初始化
* @Param0: constraintAnnotation
**/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val:vals) {
set.add(val);
}
}
/**
* @Description: 判断校验是否成功
* @Param0: value 被校验值
* @Param1: context
**/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
4)、关联自定义的校验器和自定义的校验注解
@Constraint(validatedBy = { ListValueConstraintValidator.class })
5)、使用
@NotNull(groups = {AddGroup.class, UpdateGroup.class})
@ListValue(vals = {0,1},groups = {AddGroup.class,UpdateGroup.class})
private Integer showStatus;
完整代码
controller
/**
* 保存
*/
@RequestMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
/**
* 修改
*/
@RequestMapping("/update")
public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand){
brandService.updateById(brand);
return R.ok();
}
entity
@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地址
*/
@URL(message = "logo必须是一个合法的url地址",groups={AddGroup.class,UpdateGroup.class})
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@NotNull(groups = {AddGroup.class, UpdateGroup.class})
@ListValue(vals = {0,1},groups = {AddGroup.class,UpdateGroup.class})
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty(groups={AddGroup.class})
@Pattern(regexp="^[a-zA-Z]$",message = "检索首字母必须是一个字母",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;
}
测试1:
测试2: