文章目录
- JSR 303 校验注解 @Valid
- 自定义校验器
- 错误消息提示
JSR 303 校验注解 @Valid
JSR 303 是 Java 为 Bean 数据合法性校验 提供的标准框架,它已经包含在 JavaEE 6.0 标准中。JSR 303 通过在 Bean 属性上 标注 类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
数据校验使用流程
1、引入校验依赖:spring-boot-starter-validation
2、定义封装数据的Bean
3、给Bean的字段标注校验注解,并指定校验错误消息提示
4、使用@Valid、@Validated开启校验
5、使用 BindingResult 封装校验结果
6、使用自定义校验注解 + 校验器(implements ConstraintValidator) 完成gender字段自定义校验规则
7、结合校验注解 message属性 与 i18n 文件,实现错误消息国际化
8、结合全局异常处理,统一处理数据校验错误
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
@Data
public class EmployeeAddVo {@NotBlank(message = "姓名不能为空")private String name;@NotNull(message = "年龄不能为空")@Max(value = 150, message = "年龄不能超过150岁")@Min(value = 0, message = "年龄不能小于0岁")private Integer age;@Email(message = "邮箱格式不正确")private String email;@Gender(message = "{gender.message}") //message = "{}" 占位符private String gender;private String address;private BigDecimal salary;//只要是日期:标注统一注解:@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")//默认的日期格式: 2024-09-05T08:47:58.000+00:00//反序列化:前端提交日期字符串 ===> 日期对象//序列化: 日期对象 ===> 日期字符串@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date birth;
}
但是只标注还不能起作用,在接口接收参数的时候还要加上@Valid 注解
/*** 设计模式:单一职责;* JavaBean也要分层,各种xxO:* Pojo:普通java类* Dao:Database Access Object : 专门用来访问数据库的对象* DTO:Data Transfer Object: 专门用来传输数据的对象;* TO:transfer Object: 专门用来传输数据的对象;* BO:Business Object: 业务对象(Service),专门用来封装业务逻辑的对象;* VO:View/Value Object: 值对象,视图对象(专门用来封装前端数据的对象)*** 新增员工;* 要求:前端发送请求把员工的json放在请求体中* 要求:如果校验出错,返回给前端。* {* "code": 500,* "msg": "校验失败",* "data": {* "name": "姓名不能为空", //这些就是为了让前端知道是哪些输入框错了,怎么错误,给用户要显示提示。* "age": "年龄不能超过150"* }* }* @param vo* @return*/@Operation(summary="新增员工")@PostMapping("/employee")// public R add(@RequestBody @Valid EmployeeAddVo vo, BindingResult result){public R add(@RequestBody @Valid EmployeeAddVo vo){//把vo转为do;Employee employee = new Employee();//属性对拷BeanUtils.copyProperties(vo,employee);employeeService.saveEmp(employee);return R.ok();
// if (!result.hasErrors()) { //校验通过
//
// }
// // 说明校验错误; 拿到所有属性错误的信息
// Map<String, String> errorsMap = new HashMap<>();
// for (FieldError fieldError : result.getFieldErrors()) {
// //1、获取到属性名
// String field = fieldError.getField();
// //2、获取到错误信息
// String message = fieldError.getDefaultMessage();
// errorsMap.put(field, message);
// }
// return R.error(500, "校验失败", errorsMap);}
BindingResult result的方式太麻烦了,一但出错,我们还把错误交给全局异常处理器处理。
@ExceptionHandler(value = MethodArgumentNotValidException.class)public R methodArgumentNotValidException(MethodArgumentNotValidException ex) {//1、result 中封装了所有错误信息BindingResult result = ex.getBindingResult();List<FieldError> errors = result.getFieldErrors();// 所有的属性错误Map<String, String> map = new HashMap<>();for (FieldError error : errors) {String field = error.getField(); // 错误字段String message = error.getDefaultMessage(); // 错误的默认消息map.put(field, message);}return R.error(500, "参数错误", map);}
数据校验:
- 1、导入校验包
- 2、JavaBean 编写校验注解
- 3、使用 @Valid 告诉 SpringMVC 进行校验
- 效果1: 如果校验不通过,目标方法不执行
- 4【以后不用】、在 @Valid 参数后面,紧跟一个 BindingResult 参数,封装校验结果
- 效果2: 全局异常处理机制
- 5【推荐】:编写一个全局异常处理器,处理 MethodArgumentNotValidException(校验出错的异常),统一返回校验失败的提示消息
- 6:自定义校验 = 自定义校验注解 + 自定义校验器
自定义校验器
// 校验注解 绑定 校验器
@Documented
@Constraint(validatedBy = {GenderValidator.class}) //校验器去真正完成校验功能。
@Target({ FIELD })
@Retention(RUNTIME)
public @interface Gender {String message() default "{jakarta.validation.constraints.NotNull.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };}
我们上面的Vo中就用了@Gender的自定义校验,
而我们只是标注了一个校验注解,真正的校验还是得交给校验器进行校验。
@Constraint(validatedBy = {GenderValidator.class}) 就是指定我们的校验器
在写一个校验器实现ConstraintValidator接口
public class GenderValidator implements ConstraintValidator<Gender, String> {/**** @param value 前端提交来的准备让我们进行校验的属性值* @param context 校验上下文* @return*/@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {return "男".equals(value) || "女".equals(value);}
}
错误消息提示
以前我们直接指定错误的提示,但如果现在的业务场景有变化,要求中文网站返回汉字,英文网站返回英文,怎么实现呢?
所以,我们推荐使用占位符
@Gender(message = "{gender.message}") //message = "{}" 占位符private String gender;
至于gender.message
是啥,我们进行配置,
在resource下创建messages.properties
里面写:
gender.message=性别只能为: 男, 女
jakarta.validation.constraints.Email.message=邮箱错误~~~~
那英文怎么办,copymessages.properties
这个文件,并且重命名为:messages_语言_国家区域代码.properties
一复制idea会自动Bundle在一起
在各自的语言中写各自的提示词
gender.message=Gender must be one of: Male, Female
浏览器修改语言环境,把英语移到顶部,就可以发起英语请求
请求头中会自己带上语言的
要是出现陌生国家怎么办,走默认。
但其实是鸡肋,根本不用,国际化不仅仅是修改个语言。想走向其他国家一定是做多个网站。