谷粒商城_06_JSR303校验+Elasticsearch
Posted 早上真起不来!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了谷粒商城_06_JSR303校验+Elasticsearch相关的知识,希望对你有一定的参考价值。
文章目录
谷粒商城_01_环境搭建
谷粒商城_02_Nacos、网关
谷粒商城_03_前端基础
谷粒商城_04_商品CRUD
谷粒商城_05_阿里云OSS和前端校验
JSR303校验
问题引入:填写form时应该有前端校验,后端也应该有校验
前端的校验是element-ui表单验证:https://element.eleme.cn/#/zh-CN/component/form
-
Form 组件提供了表单验证的功能,只需要通过
rules
属性传入约定的验证规则,并将 Form-Item 的prop
属性设置为需校验的字段名即可。校验规则参见 async-validator -
使用自定义校验规则可以解决字母限制、密码不一致、非整数限制等等的问题
-
基本使用
// dataForm:表单的数据、dataRule:所有检验的规则 <el-form :model="dataForm" :rules="dataRule"/> // 表单的某一项用prop指定校验的目标即可使用 <el-form-item label="密码" prop="checkPass"> // data() return dataRule: // 密码校验的字段checkPass,validator: validatePass指向校验的具体方法,既可以将其替换成方法使用 checkPass: [ validator: validatePass, trigger: 'blur' ], var validatePass = (rule, value, callback) => if (value === '') callback(new Error('请再次输入密码')); // dataForm表单的数据 else if (value !== this.dataForm.pass) callback(new Error('两次输入密码不一致!')); else callback(); ;
-
后端:@NotNull、@NotBank、@URL等等
普通校验
1、使用校验注解
在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints、org.hibernate.validator.constraints
”包中,提供了如@Email、@NotNull、@URL等注解。
- 主要看注解源码文档来了解其中使用
<!--jsr3参数校验器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.6.1</version>
</dependency>
2、@Valid内置异常
这里内置异常的意思是发生异常时返回的json不是我们的R对象,而是mvc的内置类
在controller类上加:@Validated,方法参数中加校验注解:@Valid,开启校验,
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand)
brandService.save(brand);
return R.ok();
测试: http://localhost:88/api/product/brand/save,在postman种发送上面的请求,发现返回的是json对象,但不是我们的R对象
// 其中
"defaultMessage": "不能为空",
“defaultMessage”: “不能为空”,这些错误消息定义在“hibernate-validator
”的“\\org\\hibernate\\validator\\ValidationMessages_zh_CN.properties”文件中。在该文件中定义了很多的错误规则:
javax.validation.constraints.AssertFalse.message = 只能为false
javax.validation.constraints.AssertTrue.message = 只能为true
javax.validation.constraints.DecimalMax.message = 必须小于或等于value
javax.validation.constraints.DecimalMin.message = 必须大于或等于value
javax.validation.constraints.Digits.message = 数字的值超出了允许范围(只允许在integer位整数和fraction位小数范围内)
javax.validation.constraints.Email.message = 不是一个合法的电子邮件地址
javax.validation.constraints.Future.message = 需要是一个将来的时间
javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
javax.validation.constraints.Max.message = 最大不能超过value
javax.validation.constraints.Min.message = 最小不能小于value
javax.validation.constraints.Negative.message = 必须是负数
javax.validation.constraints.NegativeOrZero.message = 必须是负数或零
javax.validation.constraints.NotBlank.message = 不能为空
javax.validation.constraints.NotEmpty.message = 不能为空
javax.validation.constraints.NotNull.message = 不能为null
javax.validation.constraints.Null.message = 必须为null
javax.validation.constraints.Past.message = 需要是一个过去的时间
javax.validation.constraints.PastOrPresent.message = 需要是一个过去或现在的时间
javax.validation.constraints.Pattern.message = 需要匹配正则表达式"regexp"
javax.validation.constraints.Positive.message = 必须是正数
javax.validation.constraints.PositiveOrZero.message = 必须是正数或零
javax.validation.constraints.Size.message = 个数必须在min和max之间
org.hibernate.validator.constraints.CreditCardNumber.message = 不合法的信用卡号码
org.hibernate.validator.constraints.Currency.message = 不合法的货币 (必须是value其中之一)
org.hibernate.validator.constraints.EAN.message = 不合法的type条形码
org.hibernate.validator.constraints.Email.message = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message = 长度需要在min和max之间
org.hibernate.validator.constraints.CodePointLength.message = 长度需要在min和max之间
org.hibernate.validator.constraints.LuhnCheck.message = $validatedValue的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message = $validatedValue的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message = $validatedValue的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message = $validatedValue的校验码不合法, $modType校验和不匹配
org.hibernate.validator.constraints.NotBlank.message = 不能为空
org.hibernate.validator.constraints.NotEmpty.message = 不能为空
org.hibernate.validator.constraints.ParametersScriptAssert.message = 执行脚本表达式"script"没有返回期望结果
org.hibernate.validator.constraints.Range.message = 需要在min和max之间
org.hibernate.validator.constraints.Safehtml.message = 可能有不安全的HTML内容
org.hibernate.validator.constraints.ScriptAssert.message = 执行脚本表达式"script"没有返回期望结果
org.hibernate.validator.constraints.URL.message = 需要是一个合法的URL
org.hibernate.validator.constraints.time.DurationMax.message = 必须小于$inclusive == true ? '或等于' : ''$days == 0 ? '' : days += '天'$hours == 0 ? '' : hours += '小时'$minutes == 0 ? '' : minutes += '分钟'$seconds == 0 ? '' : seconds += '秒'$millis == 0 ? '' : millis += '毫秒'$nanos == 0 ? '' : nanos += '纳秒'
org.hibernate.validator.constraints.time.DurationMin.message = 必须大于$inclusive == true ? '或等于' : ''$days == 0 ? '' : days += '天'$hours == 0 ? '' : hours += '小时'$minutes == 0 ? '' : minutes += '分钟'$seconds == 0 ? '' : seconds += '秒'$millis == 0 ? '' : millis += '毫秒'$nanos == 0 ? '' : nanos += '纳秒'
想要自定义错误消息,可以覆盖默认的错误提示信息,如@NotBlank的默认message是
public @interface NotBlank
String message() default "javax.validation.constraints.NotBlank.message";
可以在添加注解的时候,修改message:
@NotBlank(message = "品牌名必须非空")
private String name;
当再次发送请求时,得到的错误提示信息:
"defaultMessage": "品牌名必须非空",
但是返回的错误不是R对象,影响接收端的接收,我们可以通过局部异常处理或者统一一次处理解决
局部异常处理BindResult
- 给校验的Bean后,紧跟一个BindResult,就可以获取到校验的结果。拿到校验的结果,就可以自定义的封装。
@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)->
//获取发生错误时的message,提示信息
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();
这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理。
统一异常处理
@ExceptionHandler
- @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。
1、抽取一个异常处理类
@ControllerAdvice
标注在类上,通过“basePackages”能够说明处理哪些路径下的异常。@ExceptionHandler(value = 异常类型.class)
标注在方法上- 这里的ConstraintViolationException异常我和项目中不一样,但是可以通过源码定位到里面的属性,然后获取异常信息放入map
/**
* @author ljy
* @version 1.0.0
* @Description 集中处理所有异常
* @createTime 2021年12月14日 19:22:00
*/
@Slf4j
// @RestControllerAdvice和@ControllerAdvice的关系类似于@RestController
@RestControllerAdvice(basePackages = "com.liu.gulimall.product.controller")// 管理的controller
public class GulimallExceptionControllerAdvice
@ExceptionHandler(value = ConstraintViolationException.class) // 感知精确异常
// 也可以返回ModelAndView
// exception是controller中的异常,精确匹配ConstraintViolationException这个异常
// MethodArgumentNotValidException
public R handleValidException(ConstraintViolationException exception)
Map<String,String> map=new HashMap<>();
// 获取数据校验的错误结果,和之前一样BindingResult
exception.getConstraintViolations().forEach(item->
String message = item.getMessage();
Path propertyPath = item.getPropertyPath();
map.put(propertyPath.toString(),message);
);
log.error("数据校验出现问题,异常类型",exception.getMessage(),exception.getClass());
return R.error(400,"数据校验出现问题").put("data",map);
@ExceptionHandler(value = Throwable.class)//异常的范围更大
public R handleException(Throwable throwable)
log.error("未知异常,异常类型",
throwable.getMessage(),
throwable.getClass());
return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),
BizCodeEnum.UNKNOW_EXEPTION.getMsg());
(2)测试: http://localhost:9999/product/brand/save
(3)默认异常处理
@ExceptionHandler(value = Throwable.class)// 异常的范围更大
public R handleException(Throwable throwable)
log.error("未知异常,异常类型",
throwable.getMessage(),
throwable.getClass());
return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),
BizCodeEnum.UNKNOW_EXEPTION.getMsg());
(4)错误状态码
正规开发过程中,错误状态码有着严格的定义规则,在项目中我们的错误状态码定义
上面的用法主要是通过@Controller+@ExceptionHandler
来进行异常拦截处理,单独定义一个枚举类,用来存储这些错误状态码
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:10001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*/
/**
* @author ljy
* @version 1.0.0
* @Description TODO
* @createTime 2021年12月14日 19:23:00
*/
public enum BizCodeEnum
UNKNOW_EXEPTION(10000,"系统未知异常"),
VALID_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;
分组校验功能(多场景校验)
前面解决了统一异常处理,但是同一实体比如id,修改的时候不能为空,添加的时候为空,那怎么来校验呢?
我们采取分组的方式
1、定义分组接口,A(添加分组)、B(删除分组),接口只是标识
2、在controller方法中添加的方法中使用 @Validated(A.class)
// 表示添加的时候采用A分组策略
public R save(@Validated(A.class) @RequestBody BrandEntity brand)
3、在实体属性上,比如id,@NotNull(message = “修改id不为空”,groups = B.class)、@Null(message = “新增id必须为空”,groups = A.class)
@NotNull(message = "修改必须定制品牌id", groups = UpdateGroup.class)
@Null(message = "新增不能指定id", groups = AddGroup.class)
@TableId
private Long brandId;
在这种情况下,没有指定分组的校验注解,默认是不起作用的。想要起作用就必须要加groups。
- controller接收到之后,根据@Validated表明的分组信息,然后确定品牌对应的校验注解。
总结:
package com.liu.common.valid;
/**
* @author ljy
* @version 1.0.0
* @Description TODO
* @createTime 2021年12月14日 20:49:00
*/
public interface addGroup
========================
/**
* 保存
*/
@RequestMapping("/save")
public R save(@Validated(addGroup.class) @RequestBody BrandEntity brand, BindingResult result)
brandService.save(brand);
return R.ok();
========================
/**
* 品牌名
*/
@NotBlank(message = "品牌名不能为空", groups 以上是关于谷粒商城_06_JSR303校验+Elasticsearch的主要内容,如果未能解决你的问题,请参考以下文章
第184天学习打卡(项目 谷粒商城 26统一异常管理 JSR303分组校验)
第185天学习打卡(项目 谷粒商城27 JSR303自定义校验注解 SPU SKU 属性分组效果前端组件抽取 父子组件交互)