后端验证,需要引入validation-api-2.0.1.GA.jar、hibernate-validator-6.0.10.Final.jar和jboss-logging-3.3.2.Final.jar,具体用什么版本的jar包自行选择。
jstl.jar、standard.js用于jsp页面的标签引用
springmvc配置文件
useCodeAsDefaultMessage设置为false,如果为true则如下面的用户姓名字段会提示用户姓名长度为min~max个字符,占位符的值无法被替换;
Controller相关设置,在model前添加@Valid注解;Controller的方法中@valid对应的@ModelAttribute参数与bindingResult之间不能有参数,它们必须紧挨在一起,否则报错;(我之前就是在它们之间有HttpServletRequest参数,一直报400错误;当然你可以去掉model中验证规则的注解,只是验证将不起作用;)
@RequestMapping(value="/create", method=RequestMethod.POST)
public String createFormHandle(@Valid @ModelAttribute("formModel") UserModel formModel, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "sys/user/create";
}
try {
User entity = new User();
BeanUtils.copyProperties(formModel, entity);
CurrentUser currentUser = getCurrentUser();
entity.setCreateBy(currentUser.getUserId());
entity.setCreateDatetime(new Date());
userService.insert(entity);
} catch (Exception e) {
bindingResult.reject("User.exists", "User已经存在");
return "sys/user/create";
}
return String.format("redirect:/%s", "sys/user/list";);
}
jsp页面内容设置,这里举个例子,不一一列举。
引入:<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
验证元素注解
Bean Validation 中内置的 constraint
@Null 被注释的元素必须为 null(用于对象,如包装类型Integer、Double、Date等)
@NotNull 被注释的元素必须不为 null(用于对象,如包装类型Integer、Double、Date等)
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值(value为整型long类型)
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值(value为整型long类型)
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内(不能用于整型)
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内(min和max为整型long类型)
示例:验证字符串
/**
* 用户姓名.
*/
@NotBlank(message="用户姓名不能为空")
@Length(min=2, max=6, message="用户姓名长度为{min}~{max}个字符")
private String userName;
/**
* 用户姓名助记码.
*/
@NotBlank(message="用户姓名助记码不能为空")
@Length(max=6, message="用户姓名助记码最长为{max}个字符")
private String mnemonic;
/**
* 登录名.
*/
@NotBlank(message="")
@Length(min=2, max=6, message="登录名不能为空,且长度为{min}~{max}个字符")
private String loginName;
/**
* 登录密码.
*/
@NotBlank(message="登录密码不能为空")
@Length(max=6, message="登录密码最长为{max}个字符")
private String loginPwd;
/**
* 备注信息.
*/
@Length(max=6, message="备注最长{max}个字符")
private String remark;
jsp验证信息
字符串必填,增加@NotBlank注解。
用户姓名同时验证必填和字符串长度,出现两行验证信息;这种情况请参考登录名,将@NotBlank的message设置为空,验证信息全写在@Length的message上。
示例:验证数值
HV000030: No validator could be found for constraint 'javax.validation.constraints.Size' validating type 'java.lang.Integer'. Check configuration for 'sex'
字段上添加@Size注解,不论字段是否包装类都报该错误,基本类型的始终被当做包装类型,与是否加@NotNull和@NotBlank无关。原来是@Size只能用于String、Collection、Map和Array(测试过String类型,提示信息 个数在{min}~{max}之间,这个也是搞不懂);
整型数值
/**
* 年龄
*/
@NotNull(message="年龄不能为空")
@Range(min=18, max=65, message="年龄范围为{min}~{max}岁")
private int age;
/**
* 性别
*/
@NotNull(message="性别不能为空")
@Range(min=2, max=9, message="性别取值范围为{min}~{max}")
private Integer sex;
/**
* 状态(0:停用;1:正常;2:锁定;).
*/
@NotNull(message="状态不能为空")
@Min(value=0, message="状态不能小于{value}")
@Max(value=2, message="状态不能大于{value}")
private Integer status;
jsp验证信息
上面的年龄用基本类型,报错,空字符串无法转成int,修改为如性别的包装类型;
如:性别为null,则不进行值范围的验证;不为null才验证值范围。
示例:浮点型
@NotNull(message="工资不能为空")
@DecimalMin(value="1234.5", inclusive=true, message="工资必须大于或等于{value}")
@DecimalMax(value="6789.0", inclusive=false, message="工资必须小于{value}")
@Digits(integer=4, fraction=1)
private Double salary;
@DecimalMin和@DecimalMax只能设置值大小,不能限定小数位数;inclusive为true则包含某指,为false则不包含;
@Digits的integer表示整数位数,fraction表示小数位数;
@Digits只能设置整数位数和小数位数,而不能设置某些具体值范围(如三位数,但实际使用可能就不是刚好要100到999,可能能是更细的范围);
所以浮点型的在必要时候为@DecimalMin、@DecimalMax和@Digits的组合使用。
示例:日期
/**
* 出生日期.
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
@NotNull(message="出生日期不能为空")
@Past
private Date birthday;
/**
* 创建时间.
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
@NotNull(message="创建时间不能为空")
@PastOrPresent
private Date createDatetime;
/**
* 更新时间.
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
@NotNull(message="更新时间不能为空")
@Future
private Date updateDatetime;
/**
* 更新时间1.
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
@NotNull(message="更新时间1不能为空")
@FutureOrPresent
private Date updateDatetime1;
日期类型须配合@DateTimeFormat使用,否则提交时会提示String不能转为Date;
经测试
@Past需要一个过去的时间(为现在时间仍会出现错误提示)
@PastOrPresent需要一个过去或现在的时间
@Future需要一个将来的时间
@FutureOrPresent需要一个将来或现在的时间(为现在时间仍会出现错误提示)
示例:正则表达式
/**
* 登录密码.
*/
@Pattern(regexp="^[a-zA-Z0-9_]{6,20}$", message="登录密码只能包含字符、数字、下划线,且字符串长度为6~20")
private String loginPwd;
只须在关联对象属性上加上@Valid
public class UserModel {
/**
* 用户姓名.
*/
@NotBlank(message="用户姓名不能为空")
@Length(min=2, max=6, message="用户姓名长度为{min}~{max}个字符")
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
/**
* 用户其他信息
*/
@Valid
private UserExtModel userExtModel;
public UserExtModel getUserExtModel() {
return userExtModel;
}
public void setUserExtModel(UserExtModel userExtModel) {
this.userExtModel = userExtModel;
}
}
public class UserExtModel {
private Integer userId;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
@NotBlank(message="联系电话不能为空")
private String phone;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
private String userIdCard;
public String getUserIdCard() {
return userIdCard;
}
public void setUserIdCard(String userIdCard) {
this.userIdCard = userIdCard;
}
}
注意关联对象字段的引用
@ConvertGroup不知道怎么用
在需要验证的Controller加上@Validated,里面为验证的分组(分组内容);
public interface GroupA {
}
public interface GroupB {
}
model
@NotBlank(message="用户姓名不能为空", groups={Default.class})
@Length(min=2, max=6, message="用户姓名长度为{min}~{max}个字符", groups={GroupA.class})
private String userName;
@NotBlank(message="身份证号不能为空", groups={GroupB.class})
@Length(min=15, message="身份证最小长度{min}")
private String userIdCard;
如果添加了分组(groups),需要验证的每个注解都要加上对应分组(每个注解上的groups为数组,可添加多个分组);
任何分组都要验证的注解,要加groups={Default.class}否则不验证该注解内容。
示例:性别(GB/T 2261.1-2003(0:未知的性别;1:男性;2:女性;5:女性改(变)为男性;6:男性改(变)为女性;9:未说明的性别;));这个没法用数值范围;(参考:https://blog.csdn.net/poortess/article/details/78136170)
性别枚举
/**
* 性别(GB/T 2261.1-2003(0:未知的性别;1:男性;2:女性;5:女性改(变)为男性;6:男性改(变)为女性;9:未说明的性别;)).
* @author chensan
*
*/
public enum GenderEnum {
/**
* 未知的性别
*/
UNKNOWN(0),
/**
* 男性
*/
MALE(1),
/**
* 女性
*/
FEMALE(2),
/**
* 女性改(变)为男性
*/
FEMALE_TO_MALE(5),
/**
* 男性改(变)为女性
*/
MALE_TO_FEMALE(6),
/**
* 未说明的性别
*/
UNSTATED(9);
private Integer gender;
private GenderEnum(Integer gender) {
this.gender = gender;
}
@JsonValue
public Integer getGender() {
return this.gender;
}
@JsonCreator
public static boolean isInEnum(Integer sex) {
if (sex == null) {
return true;
}
for (GenderEnum gender : GenderEnum.values()) {
if (gender.gender.equals(sex)) {
return true;
}
}
return false;
}
}
自定义Validator
/**
* 自定义性别Validator
*/
public class CheckGenderValidator implements ConstraintValidator {
@Override
public void initialize(CheckGenderConstraint constraintAnnotation) {
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return GenderEnum.isInEnum(value);
}
}
验证器注解
/**
* 用枚举指定参数
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckGenderValidator.class)
@Documented
public @interface CheckGenderConstraint {
/**
* 用来定义默认得消息模版, 当这个约束条件被验证失败的时候,通过此属性来输出错误信息.
* @return
*/
String message() default "请填写正确的性别";
/**
* 用于指定这个约束条件属于哪(些)个校验组
* @return
*/
Class>[] groups() default {};
/**
* Bean Validation API 的使用者可以通过此属性来给约束条件指定严重级别. 这个属性并不被API自身所使用.
* @return
*/
Class extends Payload>[] payload() default {};
}
model字段引入自定义验证器注解(如果自定义验证内容参与分组,如下添加groups)
@NotNull(message="性别不能为空")
@CheckGenderConstraint(groups={GroupB.class})
private Integer sex;
model方式校验应用于web,接口校验则需要用到hibernate的校验模式(后续用到 会添加)。
问题:
当某字段不为空或为某值,才可以(且必须)填写某些内容,这些内容才验证规则,这种根据实际情况判断是否参与验证,如何动态选择验证器分组呢(或分组能解决吗)。
文章参考:https://www.cnblogs.com/mr-yang-localhost/p/7812038.html