Note that there are some explanatory texts on larger screens.

plurals
  1. POError message is not displayed in freemarker template for custom FieldMatchValidator in Spring application
    text
    copied!<p>As stated in the title I can't make the error message for the FieldMatchValidator to show in my freemarker template. All the other "regular" error messages, such as <strong>@NotNull</strong> are displayed with the accurate message from message.properties. The validation with FieldMatchValidator works since the result.hasErrors() returns true and the result objects holds the FieldMatch error. What have I missed?</p> <p><strong>applicationContext.xml:</strong></p> <pre><code>&lt;!-- Invokes Spring MVC @Controller methods --&gt; &lt;bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"&gt; &lt;property name="webBindingInitializer"&gt; &lt;!-- Configures Spring MVC DataBinder instances --&gt; &lt;bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"&gt; &lt;property name="validator" ref="validator"/&gt; &lt;/bean&gt; &lt;/property&gt; &lt;/bean&gt; &lt;bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"&gt; &lt;property name="messageInterpolator"&gt; &lt;bean class="com.reco.web.mvc.validator.CustomMessageInterpolator"&gt; &lt;property name="messageSource" ref="messageSource"/&gt; &lt;/bean&gt; &lt;/property&gt; &lt;/bean&gt; </code></pre> <p><strong>Controller:</strong></p> <pre><code>@Controller public class RegisterController extends MyWebController{ private static final String REGISTER_FORM_VIEW = "/action/register/register"; private static final String REGISTER_SUCCESS_VIEW = "/action/register/success"; private org.springframework.validation.Validator validator; @Autowired public RegisterController(MessageSource messageSource, Validator validator) { super(messageSource); this.validator = validator; } @InitBinder protected void initBinder(WebDataBinder binder) { binder.setValidator((org.springframework.validation.Validator)validator); } @RequestMapping(value = "/action/register", method = RequestMethod.GET) public ModelAndView getRegisterForm() { ModelAndView mav = new ModelAndView(REGISTER_FORM_VIEW); mav.addObject("registerForm", new RegisterForm()); return mav; } @RequestMapping(value = "/action/register", method = RequestMethod.POST) public final String register(@Valid RegisterForm registerForm, BindingResult result, ModelMap model, HttpServletRequest request) { if (result.hasErrors()) { return REGISTER_FORM_VIEW; } return "redirect:" + REGISTER_SUCCESS_VIEW; } } </code></pre> <p><strong>Annotation:</strong></p> <pre><code>@Target({TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) @Constraint(validatedBy = FieldMatchValidator.class) @Documented public @interface FieldMatch { String message() default "{constraints.fieldmatch}"; Class&lt;?&gt;[] groups() default {}; Class&lt;? extends Payload&gt;[] payload() default {}; /** * @return The first field */ String first(); /** * @return The second field */ String second(); /** * Defines several &lt;code&gt;@FieldMatch&lt;/code&gt; annotations on the same element * * @see FieldMatch */ @Target({TYPE, ANNOTATION_TYPE}) @Retention(RUNTIME) @Documented @interface List { FieldMatch[] value(); } } </code></pre> <p><strong>Validator:</strong></p> <pre><code>public class FieldMatchValidator implements ConstraintValidator&lt;FieldMatch, Object&gt; { 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); return firstObj == null &amp;&amp; secondObj == null || firstObj != null &amp;&amp; firstObj.equals(secondObj); } catch (final Exception ignore) { // ignore } return true; } } </code></pre> <p><strong>freemarker template:</strong></p> <pre><code>&lt;!doctype html&gt; &lt;html lang="sv"&gt; &lt;head&gt; &lt;meta charset="utf-8"/&gt; &lt;title&gt;&lt;/title&gt; &lt;/head&gt; &lt;body id="register"&gt; &lt;div id="content"&gt; &lt;form id="registerForm" action="${rc.contextPath}/action/register" method="POST"&gt; &lt;p&gt; &lt;label for="firstname"&gt;firstname&lt;/label&gt; &lt;@spring.formInput "registerForm.firstName" /&gt; &lt;@spring.showErrors "", "error"/&gt; &lt;/p&gt; &lt;p&gt; &lt;label for="lastname"&gt;lastname&lt;/label&gt; &lt;@spring.formInput "registerForm.lastName" /&gt; &lt;@spring.showErrors "", "error"/&gt; &lt;/p&gt; &lt;p&gt; &lt;label for="email"&gt;email&lt;/label&gt; &lt;@spring.formInput "registerForm.email" /&gt; &lt;@spring.showErrors "", "error"/&gt; &lt;/p&gt; &lt;p&gt; &lt;label for="email_again"&gt;email_again&lt;/label&gt; &lt;@spring.formInput "registerForm.confirmEmail" /&gt; &lt;@spring.showErrors "", "error"/&gt; &lt;/p&gt; &lt;p&gt; &lt;label for="password"&gt;password&lt;/label&gt; &lt;@spring.formPasswordInput "registerForm.password" /&gt; &lt;@spring.showErrors "", "error"/&gt; &lt;/p&gt; &lt;p&gt; &lt;label for="password_again"&gt;password_again&lt;/label&gt; &lt;@spring.formPasswordInput "registerForm.confirmPassword" /&gt; &lt;@spring.showErrors "", "error"/&gt; &lt;@spring.showErrors "", "confirmPassword"/&gt; &lt;/p&gt; &lt;input type="submit"/&gt; &lt;/form&gt; &lt;/div&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p><strong>FormObject:</strong></p> <pre><code>@FieldMatch.List({ @FieldMatch(first = "password", second = "confirmPassword", message = "validation.message.confirm.password"), @FieldMatch(first = "email", second = "confirmEmail", message = "validation.message.confirm.email") }) public class RegisterForm implements Serializable { private static final int MAX_TEXT_FIELD_SIZE = 32; /** * Password min size. */ private static final int PASSWORD_MIN_SIZE = 8; /** * Password max size. */ private static final int PASSWORD_MAX_SIZE = 36; @NotNull(message = "validation.message.firstname.empty") @Size(min = 1, max = MAX_TEXT_FIELD_SIZE, message = "validation.message.firstname.length") @Pattern(regexp = "^[^&lt;&gt;]*$", message = "validation.message.invalid.characters") private String firstName; @NotNull(message = "validation.message.lastname.empty") @Size(min = 1, max = MAX_TEXT_FIELD_SIZE, message = "validation.message.lastname.length") @Pattern(regexp = "^[^&lt;&gt;]*$", message = "validation.message.invalid.characters") private String lastName; @NotNull(message = "validation.message.email.empty") @Pattern(regexp = ".+@.+\\.[a-z]+", message = "validation.message.email", flags = { Pattern.Flag.CASE_INSENSITIVE }) private String email; @NotNull(message = "validation.message.email.empty") @Pattern(regexp = ".+@.+\\.[a-z]+", message = "validation.message.email", flags = { Pattern.Flag.CASE_INSENSITIVE }) private String confirmEmail; @NotNull @Size(min = PASSWORD_MIN_SIZE, max = PASSWORD_MAX_SIZE) private String password; @NotNull @Size(min = PASSWORD_MIN_SIZE, max = PASSWORD_MAX_SIZE) private String confirmPassword; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getConfirmEmail() { return confirmEmail; } public void setConfirmEmail(String confirmEmail) { this.confirmEmail = confirmEmail; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getConfirmPassword() { return confirmPassword; } public void setConfirmPassword(String confirmPassword) { this.confirmPassword = confirmPassword; } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload