网站建设的目的模板,三亚本地网站建设,旅游的网站怎么做,做网站需知文章目录 学习链接基础使用1.单个参数校验全局异常处理器 2.实体类参数校验3.嵌套校验4.分组校验5.自定义校验注解 自定义参数校验器springmvc参数校验使用方法自定义错误消息模板观察源码自定义错误消息创建ValidationMessages.properties 在Controller中使用 自定义参数校验器… 文章目录 学习链接基础使用1.单个参数校验全局异常处理器 2.实体类参数校验3.嵌套校验4.分组校验5.自定义校验注解 自定义参数校验器springmvc参数校验使用方法自定义错误消息模板观察源码自定义错误消息创建ValidationMessages.properties 在Controller中使用 自定义参数校验器CheckEmailRegisterCheckEmailRegisterValidator在controller中使用 自定义枚举传参的校验器EmailCodeTypeEnum枚举类Enums校验注解EnumsValidator 自定义枚举int值校验器EnumsTypeEnumsTypeValidator 使用示例 学习链接
spring boot mvc Validator 自定义校验(枚举值校验) SpringBoot 参数校验的方法 Spring Validation最佳实践及其实现原理 注解系列——自定义注解验证器
基础使用
1.单个参数校验
import javax.validation.constraints.NotNull;
RestController
RequestMapping(/vadmin/cart)
Validated // 开启校验在类上面不能用Valid否则下面的校验注解无效
public class CartController {RequestMapping(deleteCart)ResponseBody // 如果为null这里将会抛出异常可使用全局异常处理器捕获public Result deleteCart(NotNull Long userId, NotNull Long pid){ cartService.deleteCart(userId, pid);return Result.succ(null);}
}
// 普通参数校验(如上)在controller类上加Validated注解才能生效不要在类上使用Valid否则无效。
// 如果将Validated加在方法参数里面,普通参数校验将无效(不会做校验),
// 将Validated加载controller类上并不会引起实体类的参数校验
// 即(实体类中有属性有校验注解,但是controller的方法参数里面没有写Validated)
// 如果校验失败, 将会抛出ConstraintViolationException异常全局异常处理器
ControllerAdvice
public class MyExceptionHandler {ExceptionHandler(MyException.class)ResponseBodypublic Result myExceptionResult(MyException ex){return Result.fail(ex.getMessage());}ExceptionHandler(Exception.class)ResponseBodypublic Result exceptionResult(Exception ex){return Result.fail(内部错误: ex.getMessage());}}2.实体类参数校验
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;Data
EqualsAndHashCode(callSuper false)
TableName(t_cart)
public class Cart implements Serializable {private static final long serialVersionUID 1L;TableId(value tc_id, type IdType.AUTO)private Long tcId;private Long cartId;private Long userId;NotNull(message 商品ID不能为空)private Long pid;Min(value 1,message 至少添加一件商品)NotNull(message 必须添加商品数量信息)private Long pcount;}RequestMapping(addToCart)
ResponseBody // 启用校验 // 校验结果封装 // 后面可以接参数
public Result addToCart(Validated Cart cart, BindingResult bindingResult,Man man) {if (bindingResult.hasErrors()) {StringBuilder sb new StringBuilder();bindingResult.getAllErrors().forEach(error-{sb.append(error.getDefaultMessage() | );});throw new MyException(sb.toString());// 处理校验结果抛出异常让全局异常处理器处理自定义异常}cartService.addCart(cart);return Result.succ(null);
}
// 实体类校验注意事项
// 1. 必须把Validated写在controller方法里面写在类上面无效
// 2. 一个校验的实体类对应一个BindingResult否则会抛出MethodArgumentNotValidException异常(全局异常处理器处理)
// 如(Validated Person person,BindingResult bindingResult1,
// Validated Man man, BindingResult bindingResult) 是正确的
// 如(Validated Cart cart, BindingResult bindingResult,Man man) 是正确的
// 如(Validated Cart cart,Man man BindingResult bindingResult) 是错误的
// 第2小点总结: 一个Validated对应一个BindingResult否则抛出异常
// 3. 如果需要把错误信息配置在配置文件中
// 在springboot的resources目录下新建【ValidationMessages.properties】
// 内容为person.name.notnullperson.name用户名不能为空
// man.name.notnullman.name用户名名不能为空
// 之后即可在实体类属性上引用 NotNull(message {man.name.notnull})
// 4. 如果前面有校验注解但是后面如果不写BindingResultcontroller就会抛出异常;
// 如果写了BindingResult那么就会进controller方法的逻辑
3.嵌套校验
PostMapping(saveCareOrders) // 这里的Validated触发对CareOrderFormDto类中的注解校验
public AjaxResult saveCareOrders(Validated RequestBody CareOrderFormDto careOrderFormDto) {}Data
AllArgsConstructor
NoArgsConstructor
public class CareOrderFormDto implements Serializable {// 处方Valid // 在这里触发对CareOrderDto类中的注解校验NotNull // 如果这里不写NotNull注解, 只写Valid注解,那么如果前端如果根本就不传careOrderDto过来,// 那么就不会触发CareOrderDto类中的校验注解,传了的话,就做校验// 所以建议加上NotNull注解,先触发NotNull的非空校验,再通过Valid触发CareOrderDto类中的校验注解private CareOrderDto careOrderDto;// 处方详情Valid // 在这里触发对CareOrderItemDto类的注解校验NotEmpty(message 处方详情不能为空) // 将会触发集合不能为空的校验private ListCareOrderItemDto careOrderItemDtoList;
}4.分组校验
1.实体类
Data
public class Person { // 读取resources目录下的ValidationMessages.properties文件 、归AgroupNotNull(message {person.name.notnull},groups {Agroup.class})private String name;NotNull(message {person.age.notnull},groups {Bgroup.class})private Integer age;// 不写分组则默认属于Default.class组NotNull(message address不能为空)private String address;
}2.校验
RequestMapping(test04) // 只会校验Agroup相关的
public String test04(Validated({Agroup.class}) Person person,BindingResult result) {log.info({},{},person.getName(),person.getAge());return ok;
}RequestMapping(test05) // 只会校验Bgroup相关的
public String test05(Validated({Bgroup.class}) Person person,BindingResult result) {log.info({},{},person.getName(),person.getAge());return ok;
}RequestMapping(test06) // 只会校验Agroup、Bgroup相关的
public String test06(Validated({Agroup.class,Bgroup.class}) Person person,BindingResult result) {log.info({},{},person.getName(),person.getAge());return ok;
}RequestMapping(test07) // 校验Agroup、Bgroup再加上 没有分组的校验注解【加上默认的Default.class组的】
public String test07(Validated({Agroup.class,Bgroup.class, Default.class}) Person person, BindingResult result) {log.info({},{},person.getName(),person.getAge());return ok;
}RequestMapping(test07) // 只会校验默认组的
public String test07(Validated Person person, BindingResult result) {log.info({},{},person.getName(),person.getAge());return ok;
}5.自定义校验注解
1.自定义校验用法
package com.zzhua.controller.validate;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;Target({ElementType.TYPE,ElementType.PARAMETER})
Retention(RetentionPolicy.RUNTIME)
Constraint(validatedBy CheckLenValidator.class)
public interface CheckLen {String message() default {字符串长度不符合要求};Class?[] groups() default {};Class? extends Payload[] payload() default {};int len();
}public class CheckLenValidator implements ConstraintValidatorCheckLen, String {private int len;public void initialize(CheckLen constraint) {this.len constraint.len();}public boolean isValid(String obj, ConstraintValidatorContext context) {return obj ! null len obj.length();}
}RestController
RequestMapping(test)
Validated // 必须加这个并且加到这个位置(加到下面的test方法中无效, 因为i参数不是实体类; 实体类是加在下面方法中,才能生效// 并且这两种加的方法互不影响)
public class RoleController {GetMapping(test)public AjaxResult test(CheckLen(len3) String i) {return AjaxResult.success(hello);}
}2.内置的校验注解
// 查看 ConstraintHelper 这个类里面注册了很多 validator
// 这也是为什么内置校验注解没有校验器的原因 validatedBy{} 自定义参数校验器
springmvc参数校验使用方法
我们先总结一下在springMvc使用注解来实现参数校验的方法 在Controller接口上添加Validated可以触发在此controller类中所有使用了校验注解的接口 如 Validated // 添加此注解
RestController
public class LoginController {// 对单个参数的校验GetMapping(login/captcha)public ResultObject captcha(NotNull(message type不能为空) Integer type) {return loginService.captcha(type, request,response);}// 对自定义类型参数校验//注意这里只会校验registerDTO是否为null而不会触发在EmailRegisterDTO 类上的校验注解校验//如果需要触发对EmailRegisterDTO 类上的校验注解校验那么看下面的嵌套校验使用PostMapping(register/email) // 这里只是为了演示效果, 才把RequestBody的required属性置为false的。public ResultBoolean registerEmail(NotNull(message参数不能为空) // 此处触发了对registerDTO实体类的校验RequestBody(required false) EmailRegisterDTO registerDTO) {return loginService.registerEmail(registerDTO);}}在Controller类中的某个接口方法中在方法参数的前面加上Valid或Validate注解即可触发该参数所对应的实体类A上的属性上所添加的校验注解。如果这个实体类A上的属性还是个实体类B我们自己定义的类那么可以继续在这个实体类A的这个属性类型为实体类B上添加Valid注解来触发对嵌套的实体类B上的加了校验注解的属性校验即嵌套校验。 如 Data
public class EmailRegisterDTO {Emailprivate String email;private String emailCode;private String password;private String checkPassword;NotNull(customVo不能为空)Valid // 1. 触发嵌套校验, 不使用此注解将不会触发CustomVo上的类上使用的校验注解的校验// 2. 如果没有NotNull注解校验而只有Valid注解那就是如果前端传了customVo就校验没传,那就不校验private CustomVo customVo;NotEmpty(customVos不能为空)private ListCustomVo customVos;
}Data
public class CustomVo {NotBlank(attr不能为空)private String attr;
}// 需要在参数的前面加上Valid注解才能触发EmailRegisterDTO类上的校验注解的校验
PostMapping(register/email)
public ResultBoolean registerEmail(Valid NotNull(message参数不能为空) RequestBody(required false) EmailRegisterDTO registerDTO) {return Result.ok(true);
}自定义错误消息模板
观察源码
我们注意到源码里面就比如NotNull这个注解里面的message使用了大括号来引用消息内容。错误消息的内容可以到org/hibernate/validator/ValidationMessages_zh_CN.properties文件中找到。
Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
Retention(RUNTIME)
Repeatable(List.class)
Documented
Constraint(validatedBy { })
public interface NotNull {String message() default {javax.validation.constraints.NotNull.message};Class?[] groups() default { };Class? extends Payload[] payload() default { };/*** Defines several {link NotNull} annotations on the same element.** see javax.validation.constraints.NotNull*/Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })Retention(RUNTIME)Documentedinterface List {NotNull[] value();}
}ValidationMessages_zh_CN.properties消息的内容如下可以看到里面
可以使用{注解的属性}的方式引用注解里面的属性值可以使用${validatedValue} 的方式引用待校验的值在${..}中可以使用表达式
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 需要是一个合法的URLorg.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 纳秒}自定义错误消息
创建ValidationMessages.properties
在resources目录下创建ValidationMessages.properties文件内容可以如下
## ${validatedValue}去引用待校验的值而{min}引用校验注解实例的属性值
a.b.c ${validatedValue}-{min}这个不该为空的在类上添加校验注解并自定义消息模板内容
Data
public class EmailRegisterDTO {Length(message ${validatedValue}不是一个合法的值,min 3)NotNull(message 这个length不校验null,还得我出马)private String email;// 使用{errorMsgkey} 大括号的形式去引用写在ValidationProperties的模板消息也可以用来引用校验注解的属性值// ${validatedValue}去引用待校验的值Length(message {a.b.c}-${validatedValue}-${validatedValue.length()}-{min},min 3)private String emailCode;}在Controller中使用
Validated
RestController
public class LoginController {PostMapping(register/email)public ResultBoolean registerEmail(Valid NotNull(message参数不能为空) RequestBody(required false) EmailRegisterDTO registerDTO) {return Result.ok(true);}}自定义参数校验器
上面这种校验它只能对某个实体的的单个属性作校验但是有的时候一个实体类的2个属性是互相关联的比如注册的时候需要用户输入两次密码一个为密码第二个为确认密码前端做是必要的但后台也应该要做这时候我们只能被迫在业务代码里写了。但是我们可以通过自定义参数校验器实现
CheckEmailRegister
Target({ElementType.TYPE,ElementType.PARAMETER})
Retention(RetentionPolicy.RUNTIME)
Constraint(validatedBy CheckEmailRegisterValidator.class)
public interface CheckEmailRegister {// 可参考 ValidationMessages_zh_CN.properties 文件中的写法,这里可以用el表达式的语法去写逻辑String message() default ${validatedValue}中的${validatedValue.email}不合法,${validatedValue.emailnull||validatedValue.email?邮箱不能为空(⊙﹏⊙):};Class?[] groups() default {};Class? extends Payload[] payload() default {};}CheckEmailRegisterValidator
通过initialize方法可以获取校验注解实例可以方便的获取注解的属性可以保存下来以作后面校验使用isValid方法返回true或者false表示是否校验通过如果校验不通过将会使用校验注解的message属性作为消息模板功能同如上所述可以使用ConstraintValidatorContext提供的api来传入消息模板自定义消息错误提示
Slf4j
public class CheckEmailRegisterValidator implements ConstraintValidatorCheckEmailRegister, EmailRegisterDTO {Overridepublic void initialize(CheckEmailRegister anno) {}Overridepublic boolean isValid(EmailRegisterDTO registerDTO, ConstraintValidatorContext context) {if (registerDTO null) {return false;}try {// 这里不要给消息模板, 测试一下自定义注解的默认的消息模板if (StringUtils.isEmpty(registerDTO.getEmail()) || registerDTO.getEmail().length() 2) {return false;}if (StringUtils.isEmpty(registerDTO.getEmailCode())) {return newErrorMsg(context,邮箱验证码不能为空);}if (StringUtils.isEmpty(registerDTO.getPassword())) {return newErrorMsg(context,密码不能为空);}if (StringUtils.isEmpty(registerDTO.getCheckPassword())) {return newErrorMsg(context,密码不能为空);}if (!Objects.equals(registerDTO.getPassword(), registerDTO.getCheckPassword())) {return newErrorMsg(context,两次输入密码不一致, 一个是:${validatedValue.password}, 一个是:${validatedValue.checkPassword});}} catch (RuntimeException ex) {log.error(校验发生异常: {}, ex.getMessage());return false;}return true;}private boolean newErrorMsg(ConstraintValidatorContext context, String errMsg) {context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate(errMsg).addConstraintViolation();return false;}}在controller中使用
Validated
RestController
public class LoginController {ApiOperation(邮箱注册账号)PostMapping(web/register/email) // Controller上加了Validated注解触发了CheckEmailRegister 的校验// 如果需要触发对registerDTO的类里面的属性上的校验注解的校验这里需要加上Validpublic ResultBoolean registerEmail(CheckEmailRegister RequestBody EmailRegisterDTO registerDTO) {return loginService.registerEmail(registerDTO);}
}
自定义枚举传参的校验器
EmailCodeTypeEnum枚举类
Getter
public enum EmailCodeTypeEnum {REGISTER(1, registerTpl.ftl),FIND_PWD(2, findPwd.ftl),;private Integer type;private String templateName;EmailCodeTypeEnum(int type, String templateName) {this.type type;this.templateName templateName;}public static EmailCodeTypeEnum type(Integer type) {for (EmailCodeTypeEnum emailCodeTypeEnum : EmailCodeTypeEnum.values()) {if (Objects.equals(emailCodeTypeEnum.type, type)) {return emailCodeTypeEnum;}}throw new RuntimeException();}
}Enums校验注解
Target({ElementType.TYPE,ElementType.PARAMETER})
Retention(RetentionPolicy.RUNTIME)
Constraint(validatedBy EnumsValidator.class)
public interface Enums {String message() default 非法值:${validatedValue},请传入指定范围内的枚举值;Class?[] groups() default {};Class? extends Payload[] payload() default {};// 校验的枚举类Class? enumClazz();// 校验的枚举类的属性String enumField();// null是否合法boolean nullValid() default false;}EnumsValidator
public class EnumsValidator implements ConstraintValidatorEnums, Object {// 校验的枚举类private Class? enumClazz;// 校验的枚举类的属性private Field enumField;private boolean nullValid;Overridepublic void initialize(Enums enums) {this.nullValid enums.nullValid();Class? enumsClass enums.enumClazz();if (!enumsClass.isEnum()) {throw new RuntimeException(MessageFormat.format(%s不是枚举类, enums.enumClazz().getName()));}this.enumClazz enumsClass;try {Field field enumClazz.getDeclaredField(enums.enumField());field.setAccessible(true);this.enumField field;} catch (NoSuchFieldException e) {throw new RuntimeException(MessageFormat.format(枚举类%s,无此%s属性, enums.enumClazz().getName(), enumField));}}Overridepublic boolean isValid(Object value, ConstraintValidatorContext context) {if (value null) {return nullValid; // null是否合法}for (Object enumConstant : this.enumClazz.getEnumConstants()) {try {if (Objects.equals(enumField.get(enumConstant), value)) {return true;}} catch (IllegalAccessException e) {return false;}}ListObject enumList Arrays.stream(this.enumClazz.getEnumConstants()).map(obj - {try {return enumField.get(obj);} catch (IllegalAccessException e) {throw new RuntimeException(枚举校验发生错误);}}).collect(Collectors.toList());context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate(允许的值为: StringUtils.arrayToCommaDelimitedString(enumList.toArray())).addConstraintViolation();return false;}}
自定义枚举int值校验器
EnumsType
Target({ElementType.TYPE,ElementType.PARAMETER})
Retention(RetentionPolicy.RUNTIME)
Constraint(validatedBy EnumsTypeValidator.class)
public interface EnumsType {String message() default 非法值:${validatedValue},请传入指定范围内的枚举值;Class?[] groups() default {};Class? extends Payload[] payload() default {};int[] types();// null是否合法boolean nullValid() default false;}EnumsTypeValidator
public class EnumsTypeValidator implements ConstraintValidatorEnumsType, Integer {private ListInteger typeList new ArrayList();private boolean nullValid;Overridepublic void initialize(EnumsType enums) {int[] types enums.types();for (int type : types) {typeList.add(type);}Assert.notEmpty(this.typeList, EnumsType提供的枚举值不能为空数组);this.nullValid enums.nullValid();}Overridepublic boolean isValid(Integer value, ConstraintValidatorContext context) {if (value null) {return nullValid;}for (Object type : this.typeList) {if (Objects.equals(type, value)) {return true;}}context.disableDefaultConstraintViolation();context.buildConstraintViolationWithTemplate(非法值:${validatedValue},请传入 Arrays.toString(this.typeList.toArray())).addConstraintViolation();return false;}
}
使用示例
Validated
RestController
public class LoginController {// 下面的type参数只能传入指定的1,2GetMapping(login/captcha)public ResultMapString,Object captcha(EnumsType(types {1,2}) Integer type) {return loginService.captcha(type, request,response);}
}