springboot 各种方式的校验
springboot 有各种方式进行校验,下面一一进行介绍
1 Java断言的方式
Java1.4引入断言。可以使用这种方式做参数的校验。下面是一段做校验的例子。assert表达式为false的时候会抛出一个异常。
@PutMapping("/assert")
public Employee put2(@RequestBody Employee employee){
assert employee.getName() != null && !"".equals(employee.getName());
return employee;
}
2 Spring Assert方式
同样,使用spring Assert也可以做校验,方式与assert类似,下面是一段例子。name不能为空,否则会抛出一个异常,异常的message就是 “名称不能为空”,这种方式更好用一些,能够自定义错误消息
@PutMapping("/spring/assert")
public Employee put1(@RequestBody Employee employee){
Assert.hasText(employee.getName(), "名称不能为空");
return employee;
}
3 参数校验方式
通过javax.validation.constraints.xxx的注解对参数修饰。Controller上修饰@org.springframework.validation.annotation.Validated,如下面的例子。
@GetMapping("/param")
public Employee get1(@Size(min=3, max=5) String name, @Min(3) @Max(5) int id ){
return new Employee(id, name);
}
如果不满足条件会抛出MethodArgumentNotValidException异常,通过ControllerAdivisor可以抓取异常,向前台返回异常信息,具体返回异常情况可根据自己项目。ControllerAdivisor抓取异常方式可见上一篇,下面是例子。
@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String to400(ConstraintViolationException e){
StringBuilder result = new StringBuilder();
result.append("error 400 :");
Set<ConstraintViolation<?>> cvs = e.getConstraintViolations();
if(cvs != null){
for (ConstraintViolation cv: cvs) {
Path path = cv.getPropertyPath();
String msg = cv.getMessage();
result.append(path).append(",").append(msg).append(";");
//TODO 这里可以使用path 和msg 组合成返回的内容,这里就是做一个拼接
}
}
return result.toString();
}
4 Bean校验方式
校验Bean的方式可以使用spring的 validation。
引入validation依赖
compile('org.springframework.boot:spring-boot-starter-validation')
bean的参数上添加注解
package com.zqw.springboot.learn.boot.validate.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Data
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@Max(value=5,message = "最大5")
@Min(value = 3, message = "最小3")
private Integer id;
@Size(min=3,max=5, message = "介于3到5")
private String name;
}
Controller 注入bean时添加@Valid注解
@PutMapping("/bean")
public Employee put3(@Valid @RequestBody Employee employee){
return employee;
}
校验失败时会抛出一个MethodArgumentNotValidException 异常,可以在controlleradvisor抓取异常做处理返回前台
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String to400(MethodArgumentNotValidException e){
StringBuilder result = new StringBuilder();
result.append("error 400 :");
BindingResult bindingResult = e.getBindingResult();
List<FieldError> errors = bindingResult.getFieldErrors();
if(errors != null){
for (FieldError error: errors) {
String field = error.getField();
String msg = error.getDefaultMessage();
//TODO 这里可以使用field 和msg 组合成返回的内容,这里就是做一个拼接
result.append(field).append(",").append(msg).append(";");
}
}
return result.toString();
}
5 自定义校验
如果是java11需要添加javax.el-api和org.glassfish下的javax.el两个依赖
假设要校验身份证号,不能为空,必须为数字(暂且这么认为),必须为18位。
编写注解类,message为校验失败的默认输出,可以采用国际化的方式定义
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR,
ElementType.ANNOTATION_TYPE,ElementType.PARAMETER})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {CarNoValidator.class})
public @interface CarNoConstraint {
String message() default "{carNo.constraint.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
编写Constraint类implements ConstraintValidator<CarNoConstraint, String>,重写initialize 和isValid方法,其中initalize方法可以获取注解的参数,这里没用到所以不用获取。isValid返回校验通过与否,value就是输入的参数值。
public class CarNoValidator implements ConstraintValidator<CarNoConstraint, String> {
public void initialize(SexConstraint constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if(StringUtils.isBlank(value)) return false;
if(value.length() != 18) return false;
if(!StringUtils.isNumeric(value)) return false;//身份证号可能最后为X,先不管他,暂时认为都是数字
return true;
}
}
在使用此注解的bean上添加注解,这里还自定义了一个枚举的校验,详情可参考本文最后的github源码
@Data
@Slf4j
public class Employee {
@Max(value=5,message = "最大5")
@Min(value = 3, message = "最小3")
private Integer id;
@Size(min=3,max=5, message = "介于3到5")
private String name;
@SexConstraint
private Sex sex;
@CarNoConstraint
private String carNo;
public Employee( Integer id, String name) {
this.id = id;
this.name = name;
}
public Employee() {
}
}
国际化配置在resource下面添加文件夹i18n,文件夹下添加messages.properties和messages_zh_CN.properteis文件
sex.constraint.message=the sex is ont valid
carNo.constraint.message=the carNo must be number, the length must be 18
sex.constraint.message=\u6027\u522b\u975e\u6cd5
carNo.constraint.message=\u8eab\u4efd\u8bc1\u4e0d\u80fd\u4e3a\u7a7a\uff0c\u5fc5\u987b\u4e3a\u6570\u5b57\uff0c\u5fc5\u987b\u4e3a18\u4f4d
添加I18nConfig配置
@Configuration
public class I18nConfig {
@Bean
public LocaleResolver localeResolver() {
return new I18nLocaleResolver();
}
/**
* 获取请求头国际化信息
*/
static class I18nLocaleResolver implements LocaleResolver {
@NotNull
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String language = httpServletRequest.getHeader("content-language");
Locale locale = Locale.getDefault();
if (StrUtil.isNotBlank(language)) {
String[] split = language.split("_");
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(@NotNull HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
}
添加ValidatorConfig配置:
@Configuration
public class ValidatorConfig {
@Autowired
private MessageSource messageSource;
/**
* 配置校验框架 快速返回模式
*/
@Bean
public Validator validator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
// 设置国际化源
factoryBean.setValidationMessageSource(messageSource);
// 设置使用 HibernateValidator 校验器
factoryBean.setProviderClass(HibernateValidator.class);
Properties properties = new Properties();
// 设置 快速异常返回
properties.setProperty("hibernate.validator.fail_fast", "true");
factoryBean.setValidationProperties(properties);
// 加载配置
factoryBean.afterPropertiesSet();
return factoryBean.getValidator();
}
}
校验不通过时会抛出MethodArgumentNotValidException异常,异常抓取方式可参见bean校验处。
当然校验的注解也可以直接作用在参数上,参见第3中参数的校验。这里的校验如下:
@GetMapping("/carNo/param")
public String get1(@CarNoConstraint String carNo ){
return carNo;
}
6、 嵌套对象校验
在嵌套的属性上添加@Valid注解就好,下面是例子
package com.iscas.biz.model.deployment;
import com.iscas.common.k8s.tools.model.volume.KcVolume;
import com.iscas.common.k8s.tools.model.volume.KcVolumeClaimTemplate;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* 服务编辑信息
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/1/1 18:50
* @since jdk1.8
*/
@Data
@Accessors(chain = true)
public class DepEditor {
/**
* 基本信息
* */
@NotNull(message = "'baseEditor' {not.null}")
@Valid
private DepBaseEditor baseEditor;
/**
* 发布信息
* */
private DepPublishEditor publishEditor;
/**
* 容器配置
* */
private List<DepContainerEditor> containerEditors;
/**
* 初始化容器配置
* */
private List<DepContainerEditor> initContainerEditors;
/**服务标签*/
private List<String[]> labels = new ArrayList<>();
/**annotations*/
private List<String[]> annotations = new ArrayList<>();
/**volumes,数据卷*/
private List<KcVolume> volumes = new ArrayList<>();
/**数据卷声明(仅在statefulset模式下使用)*/
private List<KcVolumeClaimTemplate> volumeClaimTemplates = new ArrayList<>();
}
package com.iscas.biz.model.deployment;
import com.iscas.biz.valid.anno.NameConstraint;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
* 服务基本编辑信息
*
* @author zhuquanwen
* @vesion 1.0
* @date 2021/1/1 19:08
* @since jdk1.8
*/
@Data
@Accessors(chain = true)
public class DepBaseEditor {
/**名称,字母数字短横杠*/
@NotEmpty(message = "'name' {not.empty}" )
@NameConstraint
private String name;
/**副本数量*/
@Min(value = 1, message="{replication.constraint.message}")
private int repSum = 1;
/**镜像仓库密钥, 可以为空*/
private String imageRepSecret;
/**用户*/
private String username = "default";
/**容器组重启策略*/
@Pattern(regexp = "^(IfNotPresent|Always|Never)$", message = "{restart.policy.constraint.message}")
private String restartPolicy = "Always";
}
github 源码
springboot学习 上一篇 springboot异常处理
springboot学习 下一篇 自定义监听