Я использую Spring Data JPA для моделирования и проверки своих данных. В этом случае у меня есть класс, который имеет поля password и confirm:
public class RegistrationForm {
private String password;
private String confirm;
// ...
}
и теперь я хочу проверить, совпадают ли они оба. Я понял, что могу создать для этого метод, используя @AssertTrue:
@AssertTrue(message = "Passwords don't match")
private boolean isPasswordMatch() {
return password.equals(confirm);
}
Теперь в моем контроллере я проверяю этот класс, и этот метод работает нормально. Моя проблема сейчас в том, что я не могу понять, как отобразить эту ошибку в моем шаблоне Thymeleaf. Обычно я использовал это для полей с проверкой:
<span th:if = "${#fields.hasErrors('username')}" th:errors = "*{username}"></span>
Но это не работает с методами. Теперь я немного исследовал и обнаружил, что если я назову метод примерно так isXXX, то он поместит поле в экземпляр Errors с именем XXX. В данном случае это будет поле с названием passwordMatch. Я мог бы проверить это с помощью отладчика.
Это не работает, хотя эта ошибка в поле существует как ViolationFieldError, как и любая другая ошибка. Для справки я попробовал это:
<span th:if = "${#fields.hasErrors('passwordMatch')}" th:errors = "*{passwordMatch}"></span>
Я просто получаю сообщение об ошибке, сообщающее, что свойство не читается:
Caused by: org.springframework.beans.NotReadablePropertyException: Invalid property 'passwordMatch' of bean class [me.squidxtv.tacocloud.model.RegistrationForm]:
Bean property 'passwordMatch' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Примечание. В настоящее время я просматриваю 6-е издание «Spring в действии», и это появляется в главе 5 «Защита Spring», но мой вопрос не связан с этим, потому что сама книга не реализует проверку для этого класса.
@dan1st Надеюсь, все в порядке: gist.github.com/SquidXTV/f35eb14c12cfcf66827c0949af85d277
Получаете ли вы другой результат при изготовлении isPasswordMatchpublic?
@ dan1st хорошо, это работает, потому что тимелеаф считает, что этот метод является методом получения поля, поэтому th:if = "${#fields.hasErrors('passwordMatch')}" запускает его, что, однако, неверно. Не следует рассматривать это как добытчик.
Можете ли вы показать свой метод контроллера? Я думаю, что проблема может быть в аннотации @ModelAttribute (т. е. убедитесь, что вы используете @ModelAttribute("theNameOfYourAttributeHere"). Также вам может потребоваться доступ к глобальным ошибкам в Thymeleaf.
@dan1st Это мой класс контроллера: gist.github.com/SquidXTV/d72692d74e4e2d7106f90e352097dd59 Обратите внимание, что все остальные варианты использования RegistrationForm работают, как пример имени пользователя в исходном вопросе.




Если вы используете #fields.hasErrors('passwordMatch'), он ожидает свойство passwordMatch, поэтому одним из способов является создание метода isPasswordMatch()public. Вероятно, это самый простой способ сделать это. При этом вам может потребоваться изменить password.equals(confirm) на Objects.equals(password, confirm).
Однако Thymeleaf также позволяет вам получить доступ ко всем ошибкам без проверки полей, используя #fields.errors() или #fields.detailedErrors().
Если вы хотите перебрать все ошибки, вы можете сделать это, используя #fields.errors():
<div th:if = "${#fields.hasErrors()}" th:each = "err : *{#fields.errors()}">
<!-- Tell the user about the error -->
<span th:text=${err}></span>
</div>
Однако это не позволяет фильтровать конкретные ошибки. Если вы хотите использовать информацию о том, что произошла ошибка passwordMatch, вам нужно использовать DetailErrors(). Вы можете перебрать это следующим образом:
<div th:if = "${#fields.hasErrors()}" th:each = "err : *{#fields.detailedErrors()}">
Error in <span th:text=${err.fieldName}></span>: <span th:text=${err.message}></span>
</div>
С помощью этой информации вы также можете фильтровать passwordMatch:
<th:block th:if = "${#fields.hasErrors()}" th:each = "err : *{#fields.detailedErrors()}">
<div th:if = "${'passwordMatch'.equals(err.fieldName)}" th:text=${err.message}></div>
</th:block>
Публикация метода работала правильно только в том случае, если я предварительно инициализировал поле password. В противном случае он дал NPE, потому что теперь он может работать вне контекста @Valid.
Этого не было, когда я тестировал это, но это звучит разумно. Вместо этого вы также можете использовать Objects.equals. В любом случае я думаю, что поведение #fields и @AssertTrue, вероятно, непреднамеренно, поэтому, возможно, стоит написать отчет об ошибке. На стороне Тимелеафа. Учитывая трассировку стека (по крайней мере, ту, которую я получил), похоже, что это какое-то взаимодействие между Spring, сообщающим Thymeleaf об ошибке поля, и затем Thymeleaf, запрашивающим информацию об этом свойстве. Думаю, github.com/thymeleaf/thymeleaf-spring/issues, вероятно, будет подходящим местом, чтобы сообщить об этом.
Можете ли вы показать полную версию
ViolationFieldError, которую видите в отладчике? Я думаю, что конкретно свойствоfieldможет быть актуальным.