使用JSR303做后台数据校验是有效确保非法数据绕过前端提交数据,必须确保数据的有效性和准确性。方法步骤如下:
一、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.6.6</version>
</dependency>
二、在实体类上加入注解
1.@NotNull:不能为null,但可以为empty (“”,”“,”“)
2.@NotEmpty:不能为null,而且长度必须大于0 (”“,”“)
3.@NotBlank:只能作用在String上,不能为null,而且调用trim()后,长度必须大于0
/**
* 品牌
*
* @author javahorse
* @email javahorse@gmail.com
* @date 2022-06-22 17:30:51
*/
@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地址
*/
@NotBlank(message = "品牌logo不能为空")
@URL(message = "品牌logo必须是合格的http地址")
private String logo;
/**
* 介绍
*/
@NotBlank(message = "品牌介绍不能为空")
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@NotNull(message = "显示状态不能为空")
@Max(value = 1, message = "显示状态不能大于1")
@Min(value = 0, message = "显示状态不能小于0")
private Integer showStatus;
/**
* 检索首字母
*/
@NotBlank(message = "检索首字母不能为空")
@Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是英文字母")
private String firstLetter;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
@Min(value = 0, message = "排序必须大于等于0")
private Integer sort;
}
三、后台 controller 中验证提交的信息
提交参数中加入@Valid注解,并通过BindingResult收集验证错误信息。
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
if(result.hasErrors()){
Map<String, String> map = new HashMap<>();
result.getFieldErrors().forEach(item -> {
// 获取验证不通过字段名
String field = item.getField();
// 获取验证不通过字段信息
String message = item.getDefaultMessage();
map.put(field, message);
});
return R.error(400, "提交数据不合法").put("data", map);
}else{
brandService.save(brand);
return R.ok();
}
}
四、通过postman绕过前端测试
五、为了方便在每个controller中使用校验方法,通过AOP切面将校验异常及其他异常整合成一个类。
/**
* 集中处理 controller中的异常
*/
@Slf4j
@RestControllerAdvice(basePackages = "com.javahorse.gmall.product.controller")
public class gmallExceptionControllerAdvice {
/**
* 处理验证异常
* @param e
* @return
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handValidException(MethodArgumentNotValidException e) {
log.error("数据校验异常:{},异常类型:{}", e.getMessage(), e.getClass());
BindingResult bindingResult = e.getBindingResult();
Map<String, String> errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach(item -> {
// 获取验证不通过字段名
String field = item.getField();
// 获取验证不通过字段信息
String message = item.getDefaultMessage();
errorMap.put(field, message);
});
return R.error(BizCodeEnum.VAILD_EXCEPTION.getCode(), BizCodeEnum.VAILD_EXCEPTION.getMsg()).put("data", errorMap);
}
/**
* 处理不能精确匹配的其他异常
* @param throwable
* @return
*/
@ExceptionHandler(value = Throwable.class)
public R handValidException(Throwable throwable) {
return R.error(BizCodeEnum.UNKNOW_EXCEPTION.getCode(), BizCodeEnum.UNKNOW_EXCEPTION.getMsg());
}
}
六、设置通用的错误代码及信息枚举类
/**
* @desc: 通用错误代码及信息类
*/
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;
}
}
七、修改原先的controller,由于通过AOP切面方式校验,所以不需要在controller中 BindingResult,只需要@Valid 注解就可以通过切面类捕获抛出的异常来达到校验的结果,代码修改如下:
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
八、测试
msg和code已经变成切面类定义的错误信息了。