Julius
Julius

Reputation: 2864

Validation messages not picked up from message properties file in Spring

I had it working yesterday and then I did something and now I have been trying to fix it for hours and I just can't get it to work anymore.

I have a Spring MVC app containing a <form:form> that I want to display custom error messages (<form:errors>) from a .properties file when the user types in wrong information. What 'wrong' is is defined in JSR-303 annotations.

Excerpt from the form:

<form:form method="post" action="adduserprofile" modelAttribute="bindableUserProfile">
<table>
    <tr>
        <td><form:label path="firstName">Voornaam</form:label></td>
        <td>
            <form:input path="firstName"/>
            <form:errors path="firstName" />
        </td>
    </tr>
    <tr>
        <td><form:label path="lastName">Achternaam</form:label></td>
        <td>
            <form:input path="lastName"/>
            <form:errors path="lastName" />
        </td>
    </tr>

Excerpt from the BindableUserProfile:

   @NotNull
@Size(min = 3, max = 40, message="{errors.requiredfield}")
public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

@NotNull
@Size(min = 3, max = 40,  message="errors.requiredfield")
public String getLastName() {
    return lastName;
}

Excerpt from the controller:

    @RequestMapping(value = "/edit/{userProfileId}", method = RequestMethod.GET)
public String createOrUpdate(@PathVariable Long userProfileId, Model model) {
    if (model.containsAttribute("bindableUserProfile")) {
        model.addAttribute("userProfile", model.asMap().get("bindableUserProfile"));
    } else {
        UserProfile profile = userProfileService.findById(userProfileId);
        if (profile != null) {
            model.addAttribute(new BindableUserProfile(profile));
        } else {
            model.addAttribute(new BindableUserProfile());
        }
    }

    model.addAttribute("includeFile", "forms/userprofileform.jsp");
    return "main";
}

@RequestMapping(value = "/adduserprofile", method = RequestMethod.POST)
public String addUserProfile(@Valid BindableUserProfile userProfile, BindingResult result, Model model) {
    if (result.hasErrors()) {
        return createOrUpdate(null, model);
    }

    UserProfile profile = userProfile.asUserProfile();
    userProfileService.addUserProfile(profile);
    return "redirect:/userprofile";
}

Excerpt from application-context.xml

   <bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="messages/messages"/>
</bean>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="validationMessageSource">
        <ref bean="messageSource"/>
    </property>
</bean>

In resources/messages I have two files, messages_en.properties and messages_nl.properties. Both have the same, simple content:

errors.requiredfield=This field is required!!!

So, it seems the properties files are found and the validation annotations are properly processed, but Spring doesn't understand it must replace the message keys with messages from the properties files.

Upvotes: 6

Views: 21156

Answers (2)

Shan Arshad
Shan Arshad

Reputation: 151

I have been doing the same thing from past 1.5 days and finally i found a solution of that.

May be it sounds a bit crazy but its a working solution. :)

@Size(min = 1, max = 50, message = "Email size should be between 1 and 50")

Now remove message = "Email size should be between 1 and 50" from validation tag.

After doing this your annotation will be like this.

@Size(min = 1, max = 50)

Now at controller side debug the method which is being called upon when submitting the form. Below is my method which is receiving the request when user hits submit.

public static ModelAndView processCustomerLoginRequest(IUserService userService, LoginForm loginForm, 
        HttpServletRequest request, HttpSession session, BindingResult result, String viewType, Map<String, LoginForm> model)

Now place a debug point at very first line of the method and debug the argument "result".

BindingResult result

While dubugging you will find a string like this in codes array.

Size.loginForm.loginId

Now define this string in your properties file and a message against that string. Compile and execute. That message will be displayed whenever that annotation wouldn't be validated.

Size.loginForm.loginId=email shouldn't be empty.

Basically spring makes its own string as key to its property file message. In above key:

  • Size(@Size) = validation annotation name
  • loginForm = my class name
  • loginId = property name in loginForm class.

The beauty of this method is it also runs fine while you will be using Spring Internationalization. It automatically switches the messages file as language changes.

Upvotes: 2

Julius
Julius

Reputation: 2864

Yaaaargh, I think this was never supposed to work in the first place.

I thought it was possible to have the "message" attribute of JSR-303 annotations to be interpreted as a key in order to get an associated error message from a message.properties file, but I thinnk I am wrong.

@Size(min = 3, max = 40, message="errors.requiredfield")

My collegue at work programmed a layer that created this behaviour for us, but it doesn't work by default. It seemed as if I had it working once, because I was using

@Size(min = 3, max = 40, message="{errors.requiredfield}")

The curly braces caused Spring to start a find and replace procedure that uses .properties files as a source. This second option still worked though.

Upvotes: 3

Related Questions