使用elementUI的组件来进行校验,查看其文档。Form表单->表单验证->自定义校验规则
firstLetter: [
// { required: true, message: '检索首字母不能为空', trigger: 'blur' }
{
validator: (rule, value, callback) => {
if (value === '') {
callback(new Error("检索首字母不能为空"))
}else if(!/^[A-z]$/.test(value)){
callback(new Error("必须是字母"))
}
callback()
},
trigger: 'blur'
}
],
格式参考文档的格式。validator: (rule, value, callback) => {}是elementUI提供的方法
else if( )里面是正则表达式,取反“!”表示不满足该表达式则应该报错。
callback()是回调函数,添加内容,在界面上提示报错信息。
sort: [
// { required: true, message: '排序不能为空', trigger: 'blur' }
{
validator: (rule, value, callback) => {
if (value === '') {
callback(new Error("排序不能为空"))
} else if (!/^([1-9]+\d*|0)$/.test(value)) {
callback(new Error("必须是正整数或0"))
}
callback()
},
trigger: 'blur'
}
]
注意格式,和正则表达式的使用。
使用JSR303,和Hibernate Validate(前一种的扩展)
@URL(message="回显指定信息") Url校验,不通过则返回提示message。参数下同。
@Pattern(regexp = "^[A-z]$",message = "必须是大小写的英文字母") 自定义匹配。
用于String类型,匹配正则表达式。不能用于Integer等类型。
为什么这儿的正则表达式,又不用/^[A-z]$/斜杠把它们包起来了?反而会出错??
@NotNull
适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为 Empty)
@NotBlank
适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0;
不能用于Integer等数值类型上。
@NotEmpty
适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0
注意事项和使用细节
如果校验注解不指定message属性,会返回默认消息,这些消息是配置在ValidationMessages_zh_CN.properties文件中的(使用Ctrl+N来进行查找)。这个文件是框架提供的。
在方法的参数前加@Validated,否则不进行验证。添加BindingResult result参数,用于显示错误信息。
引入背景:
可以发现,后端的验证,需要在每一个controller的path里面进行一次判断,重复量大,效率还低。
这个时候,我们可以使用@ControllerAdvice统一管理异常/错误。它是一个全局异常处理类/器。
关联回忆:
SpringMVC
使用方法:
新建一个类,并给它加上一个注解:@ControllerAdvice
在具体的方法上加注解:@ExceptionHandler({指定需要处理的异常类})
方法参数的类型跟,处理异常的类,是同一个
注意在controller层的注解@Validated依然需要,否则不起作用
/**
* @ControllerAdvice 处理异常。该参数指向对哪个包生效
* @Slf4j 输出日志
* @ResponseBody 发送json格式的数据
* @ExceptionHandler 参数说明对哪些异常生效
*/
@ControllerAdvice(basePackages = "com.stein.steinliving.commodity.controller")
@Slf4j
@ResponseBody
public class SteinLivingExceptionHandler {
@ExceptionHandler({MethodArgumentNotValidException.class})
public R exceptionHandler(MethodArgumentNotValidException e){
HashMap map = new HashMap<>();
e.getBindingResult().getFieldErrors().forEach(item->{
map.put(item.getField(),item.getDefaultMessage());
});
return R.error(400,"验证异常,请按要求输入").put("data",map);
}
}
验证异常也是异常的一种,所以引出异常的处理。
Throwable.class是所有异常的父类,把它放在后面,这样前面没有处理的异常,都可以用它来接收。
通过Ctrl+H查看Throwable的继承关系,可以看到子类所有的异常类有哪些。这些类都是可以根据需要选填在@ExceptionHandler({MethodArgumentNotValidException.class})这个参数里面的
参数类型,这儿也是和注解的参数类型保持了一致。(同上)
@ExceptionHandler({Throwable.class})
public R errorHander(Throwable throwable){
return R.error(40000,"系统未知错误");
}
统一规定错误/状态码异常
问题分析:公司写代码会统一规定错误/异常状态码和提示信息,我们可以将其抽取成枚举类,方便管理和扩展
错误/异常状态码如何规定,不同公司不太一样,遵守项目经理给的规则即可。比如模块编号+错误/异常编号
实现过程:
在common模组中创建Enum类,定义好要求的code和msg,再在原有代码的
R.error(枚举.xxx.getCode( ),枚举.xxx.getMsg( ) )即可
public enum EnumExceptionCode {
UNKNOWN_EXCEPTION(40001,"系统未知异常"),
INVALID_EXCEPTION(40000,"验证未通过");
private int code;
private String msg;
EnumExceptionCode() {
}
EnumExceptionCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
getter and setter...
枚举的重点回忆:
a. 类的类型选择Enum枚举类型
b. 枚举的常量,写在类体的最开头。常量名建议大写。
c. 有多个常量,使用","逗号分隔
d. 常量的参数,跟构造器里面的一样。
在不同的需要场景,校验规则不一样。比如在修改的时候,要校验id;而新增的时候,id不用校验。
新建一个接口来定义一个分组,通过接口名来区分分组。
经测试,必须使用接口。如果是一个类,会报错。
public interface SaveGroup {
}
//这是两个接口的代码写在了一起
public interface UpdateGroup {
}
在调用的Controller的映射路径上,@Validated({添加分组的接口名.class}),来确定使用哪个分组
public R save(@Validated({SaveGroup.class}) @RequestBody BrandEntity brand)
public R update(@Validated({UpdateGroup.class})@RequestBody BrandEntity brand)
在Entity的校验规则注解上,添加group属性,确定加入哪些分组
@NotBlank(message = "必须输入查询首字母",groups = {SaveGroup.class})
@Pattern(regexp = "^[A-z]$",message = "必须是大小写的英文字母",groups={SaveGroup.class,UpdateGroup.class})
private String firstLetter;
/**
* 排序
*/
@NotNull(message = "必须输入能排序的正整数",groups = {SaveGroup.class})
// @Pattern(regexp = "/^([1-9]\\d*|0)$/",message = "必须是正整数")这个写法会报错
@Min(value = 0,message = "必须大于等于0",groups = {SaveGroup.class,UpdateGroup.class})
private Integer sort;
注意:没有分组的Entity校验规则,在指定了@Validated({分组名})的映射路径下,不会生效!
/**
* logo
* 这儿@NotBlank故意没有设置分组,用来说明此处注解不分组,而@Validated({设置了分组}),就不会生效
*/
@NotBlank(message = "logo不能为空")
@URL(message = "这不是一个url地址",groups={SaveGroup.class,UpdateGroup.class})
private String logo;
@Pattern 正则表达式的校验不能用在数值类型上面,比如Integer。原有注解又不能满足定制需要,比如isshow是否显示,要求验证只能输入0-1两个值。
说明:自定义校验器/注解,全程可以参考@NotNull源码来编写
因为这个校验在各个模块都可能使用,因此在steinliving-common模块开发
修改steinliving\steinliving-common\pom.xml,引入自定义校验器相关jar
javax.validation
validation-api
2.0.1.Final
新建一个自定义的注解,比如@EnumValidated。内部结构可以参考@NotNull
添加values属性,注意类型
添加message的默认值。文件名src/main/resources/ValidationMessages.properties
该资源的名字必须以此命名,校验器才能找到它,配置才能生效。
赋值必须使用unicode
com.stein.common.validate.EnumValidated.message=\u7ed3\u679c\u53ea\u80fd\u662f0\u6216\u80051
新建自定义验证类实现接口
偶然学到的一个小技巧:
在IDEA中,使用Alt+左键拖选,可以选择局部多行,简直不要太便捷。