我只是每个字段只收到一条错误消息。我对每个字段都有很多规则,我想逐个验证它们。如果一个失败,验证将停止并仅返回一条描述此字段失败规则的消息。
经过研究,我发现了@ReportAsSingleViolation注释之类的东西,它有点起作用,但它有来自自定义约束的固定消息。所以这不是我想要的。
我读过有关@GroupSequence的信息,但我无法像我所描述的那样工作。
这是我的自定义约束规则实体:
@Entity
@Table(name = "users", schema = "myschema")
public class User {
private int id;
@ValidLogin
private String login;
@ValidPassword
private String password;
@ValidEmail
private String email;
//getters & setters
}
使用几个内置规则实现我的自定义约束:
@Constraint(validatedBy = UsernameValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@NotEmpty
@Pattern(regexp = "^[a-zA-Z0-9]*$")
@Length.List({
@Length(min = 3 , message = "{Length.min.user.login}"),
@Length(max = 30, message = "{Length.max.user.login}")
})
public @interface ValidLogin {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default { };
}
默认情况下,我在jsp视图中收到失败规则的每条消息。所以,我想让它像这样工作:检查规则@NotEmpty,如果失败,返回相应的消息,如果不验证下一个规则@Pattern,依此类推。
你可以帮忙吗?非常感谢!答案 0 :(得分:1)
我认为,您正在寻找以下内容:
@Test
public void test() {
Validator v = Validation.byProvider( HibernateValidator.class )
.configure()
.buildValidatorFactory()
.getValidator();
// validations for each group - shows only corresponding violations even if other constraints
// are violated as well
assertThat( v.validate( new Bar( null, null ), First.class ) ).hasSize( 2 );
assertThat( v.validate( new Bar( "", "" ), Second.class ) ).hasSize( 2 );
assertThat( v.validate( new Bar( "a", "a" ), Third.class ) ).hasSize( 2 );
// shows that validation will go group by group as defined in the sequence:
//NotNull
Set<ConstraintViolation<Bar>> violations = v.validate( new Bar( null, null ) );
assertThat( violations ).hasSize( 2 );
assertThat( violations ).extracting( "message" ).containsOnly( "must not be null" );
//NotBlank
violations = v.validate( new Bar( "", "" ) );
assertThat( violations ).hasSize( 2 );
assertThat( violations ).extracting( "message" ).containsOnly( "must not be blank" );
//Size
violations = v.validate( new Bar( "a", "a" ) );
assertThat( violations ).hasSize( 2 );
assertThat( violations ).extracting( "message" ).containsOnly( "size must be between 5 and 2147483647" );
}
@GroupSequence({ First.class, Second.class, Third.class, Bar.class })
private static class Bar {
@NotNull(groups = First.class)
@NotBlank(groups = Second.class)
@Size(min = 5, groups = Third.class)
private final String login;
@NotNull(groups = First.class)
@NotBlank(groups = Second.class)
@Size(min = 5, groups = Third.class)
private final String password;
public Bar(String login, String password) {
this.login = login;
this.password = password;
}
}
interface First {
}
interface Second {
}
interface Third {
}
我添加了一个测试,因此可以看到验证如何逐组进行。要有这样的行为,您需要重新定义bean的默认组序列。为此,您需要在您要验证的bean上放置@GroupSequence
注释,然后列出您需要的所有组,并且不要忘记添加bean类本身(如本例所示)。此外,所有这些信息都存在here - 文档中。
如果您没有使用标准约束,那么您可能会执行以下操作:
@Test
public void test2() throws Exception {
Set<ConstraintViolation<Foo>> violations = validator.validate( new Foo( "", null ) );
assertThat( violations ).hasSize( 2 );
assertThat( violations ).extracting( "message" )
.containsOnly( "value should be between 3 and 30 chars long", "Value cannot be null" );
}
private static class Foo {
@ValidLogin
private final String login;
@ValidLogin
private final String password;
public Foo(String login, String password) {
this.login = login;
this.password = password;
}
}
@Target({ FIELD })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { ValidLogin.ValidLoginValidator.class })
@interface ValidLogin {
String message() default "message";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
class ValidLoginValidator implements ConstraintValidator<ValidLogin, String> {
private static final Pattern PATTERN = Pattern.compile( "^[a-zA-Z0-9]*$" );
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
String message = "";
if ( value == null ) {
message = "Value cannot be null";
}
else if ( !PATTERN.matcher( value ).matches() ) {
message = "Value should match pattern ";
}
else if ( message.length() < 3 || message.length() > 30 ) {
message = "value should be between 3 and 30 chars long";
}
if ( !message.isEmpty() ) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate( message ).addConstraintViolation();
}
return false;
}
}
}
在这种情况下,您只需拥有自己的自定义约束和验证器。然后你通过检查检查,然后根据第一次失败的检查建立违规。如果您对登录和密码执行类似的检查,但是例如在字符串长度上具有不同的模式,您还可以将pattern
和min
,max
之类的内容作为属性提取到约束中。
答案 1 :(得分:0)
每个注释都有groups
属性,可用于将检查划分为组:
public class MyForm {
@NotEmpty(groups = LoginLevel1.class)
@Pattern(regexp = "^[a-zA-Z0-9]*$", groups = LoginLevel2.class)
@Length.List({
@Length(min = 3 , message = "{Length.min.user.login}", groups = LoginLevel3.class),
@Length(max = 30, message = "{Length.max.user.login}", groups = LoginLevel3.class)
})
private String login;
}
下一步是将这些组分组为允许具有快速失败行为的@GroupSequence
:
public class MyForm {
...
@GroupSequence({
LoginLevel1.class,
LoginLevel2.class,
LoginLevel3.class
})
public interface LoginChecks {}
public interface LoginLevel1 {}
public interface LoginLevel2 {}
public interface LoginLevel3 {}
}
最后一步是指示Spring使用这些组序列进行验证:
@PostMapping("/form-handler")
public String processForm(@Validated({MyForm.LoginChecks.class, MyForm.PasswordChecks.class}) MyForm form, BindingResult result) {
if (result.hasErrors()) {
return null;
}
...
}
答案 2 :(得分:0)
我想知道GroupSequence是否由于自定义约束而无法工作,这就是原因。
我在自定义约束中使用内置规则,在那里,他们的小组不能工作。在自定义约束中只能看到一个组DEFAULT,因为:
Class<?>[] groups() default { };
当我将这些内置规则从自定义约束移动到字段(现在更难看,我想让Entity保持漂亮)时,这是有效的。
但是我们又来了。现在它必须进入&#34;水平&#34; by&#34; level&#34;意味着当一个字段为空白而其他字段为空时,只有一个空白消息。其他人甚至认为他们无效,正在等待下一个&#34;等级&#34;序列。这又是 - 不是我想要的。
似乎每个字段获得一个错误对于spring / hibernate来说太多了。
如果有人想知道如何让它发挥作用,请让我知道,我会尝试一下。