2015-09-16 2 views
1

Я пытался настроить пользовательскую проверку валидации javax (Spring Boot & Thymeleaf), но я не могу понять, как отобразить сообщение об ошибке. Кажется, что проблема заключается в том, что «обычные» ошибки (например, @Size, @NotNull и т. Д.), Похоже, добавляют результат FieldError к результату привязки. Однако мой пользовательский валидатор предоставляет ObjectError. Я не могу понять, как заставить Thymeleaf отображать мою пользовательскую ошибку (которая, очевидно, проходит, так как показывает th:errors="*{*}").Spring + Thymeleaf custom validation display

Любая помощь очень ценится.

UPDATE: теперь я могу отобразить сообщение об ошибке, однако через

<p th:if="${#fields.hasErrors('${user}')}" th:errors="${user}"></p> 

, если мне нужно больше, чем один валидатор (например, подтвердить пароль и подтвердить адрес электронной почты) это решение не будет работать (или отображать как сообщения об ошибках ., если один не подходит не стесняйтесь, если у вас есть предложение

Ниже приведен код, который я использовал для этого:.

Шаблон:

Реализация
<p th:if="${#fields.hasErrors('username')}"th:errors="*{username}"></p> 
<!-- works just fine --> 
<p th:if="${#fields.hasErrors('*')}" th:errors="*{*}"></p> 
<!-- works and displays all errors (for every field with an error, 
including my custom validator) --> 
<p th:if="${#fields.hasErrors('confirmPassword')}" th:errors="*{*}"></p> 
<!-- does not work --> 
<p th:if="${#fields.hasErrors('*')}" th:errors="*{confirmPassword}"></p> 
<!-- does not work --> 

Валидатор:

public class PasswordsEqualConstraintValidator implements 
     ConstraintValidator<PasswordsEqualConstraint, Object> { 

    @Override 
    public void initialize(PasswordsEqualConstraint arg0) { 
    } 

    @Override 
    public boolean isValid(Object candidate, ConstraintValidatorContext arg1) { 
     User user = (User) candidate; 
     return user.getPassword().equals(user.getConfirmPassword()); 
    } 
} 

Валидатор интерфейс:

@Target({ ElementType.TYPE }) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
@Constraint(validatedBy = PasswordsEqualConstraintValidator.class) 
public @interface PasswordsEqualConstraint { 
    String message(); 

    Class<?>[] groups() default {}; 

    Class<? extends Payload>[] payload() default {}; 
} 

User.java:

@PasswordsEqualConstraint(message = "passwords are not equal") 
public class User implements java.io.Serializable { 
...  
@Size(min=2, max=40) 
private String username; 
... 
private String confirmPassword; 
... 

Контроллер:

@RequestMapping(value = "/signup", method = RequestMethod.POST) 
public String signup(@Valid User user, BindingResult bindingResult) { 
     if (bindingResult.hasErrors()) { 
      return "signup"; 
     } 
     ... do db stuff .. return "success"; 
} 

ответ

3

Возможно, это связано с тем, что ваш @PasswordsEqualConstraint присваивается всему компоненту (типу), а не полем «confirmPassword». Чтобы добавить возможное нарушение ограничений в конкретное поле, вы можете сделать, например, пример.

FieldMatch сравнивает два поля, если они не равны, а вторая ошибка присваивается ошибке проверки.

BTW. это более общее решение для того, что вы делаете. Fo например, для паролей вы можете использовать его как

@FieldMatch(first = "password", second = "confirmPassword", message = "Passowords are not equal.") 

валидатора:

public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> { 

    private String firstFieldName; 
    private String secondFieldName; 

    @Override 
    public void initialize(final FieldMatch constraintAnnotation) { 
    firstFieldName = constraintAnnotation.first(); 
    secondFieldName = constraintAnnotation.second(); 
    } 

    @Override 
    public boolean isValid(final Object value, final ConstraintValidatorContext context) { 
    try { 
     final Object firstObj = BeanUtils.getProperty(value, firstFieldName); 
     final Object secondObj = BeanUtils.getProperty(value, secondFieldName); 

     boolean isValid = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj); 

     if (!isValid) { 
     context.disableDefaultConstraintViolation(); 
     context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addNode(secondFieldName).addConstraintViolation(); 
     } 

     return isValid; 
    } 
    catch (final Exception ignore) { 
     // ignore 
    } 
    return true; 
    } 
} 
+1

Работает как очарование, спасибо большое! Одно небольшое изменение в вашем ответе - использовать addPropertyNode() вместо устаревшего метода addNode(). – Lukehey

0

я, наконец, получил утвержденный ответ на работу, но только после получения деталей из первой секции вопроса. Сначала было неясно, как определить интерфейс @FieldMatch. Главным образом, что

@Target({ElementType.TYPE}) 
@Retention(RetentionPolicy.RUNTIME) 
@Constraint(validatedBy = FieldMatchValidator.class) 

необходимо в интерфейсе FieldMatch.

Вот весенняя документация. http://dolszewski.com/spring/custom-validation-annotation-in-spring/

Весенняя документация показывает @Target ({ElementType.METHOD, ElementType.FIELD})

, который меня испортил, потому что цель была методом и полем, когда isValid получил имя Object только когда-либо значение атрибута 1, на которое я помещал аннотацию @FieldMatch. Но имея @Target ({ElementType.TYPE)) Затем, когда вызывается Valid, я получаю весь бит, который проверяется, и я могу либо применить его, либо использовать отражение, чтобы получить значения.