Adding error message to Spring 3 DataBinder for custom object fields

Thomas Buckley picture Thomas Buckley · Aug 24, 2012 · Viewed 52.7k times · Source

I'm trying to manually add an individual email error message to my model but nothing is displaying in the view.
I think it may be how I'm creating or attached the ObjectError to the BindingResult.
I'm adding the error inside the catch.

Here is the contents of result.errors when I leave email field empty and JSR-303 annotations kick in (error displays in view):

[Field error in object 'user' on field 'email': rejected value []; codes [NotEmpty.user.email,NotEmpty.email,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email]]; default message [may not be empty]]


Here is the contents of of result.errors after I manually add the ErrorObject(email error does not display in view):

[Error in object 'email': codes []; arguments []; default message [An account already exists for this email.]]


Controller:

@RequestMapping(value = "/registration", method = RequestMethod.POST)
    public ModelAndView post(@Valid User user, BindingResult result)
    {

        if (result.hasErrors())
        {
            ModelAndView modelAndView = new ModelAndView(
                    Consts.MODEL_RESISTER_PAGE);
            modelAndView.addObject("user", user);
            return modelAndView;
        }
        else
        {
            try
            {
                userService.addUser(user);

                ModelAndView modelAndView = new ModelAndView(
                        Consts.MODEL_CARD_REPORTS_HOME_PAGE);
                modelAndView.addObject("userRegisteredSuccess", Boolean.TRUE);

                return modelAndView;
            }
            catch (DataIntegrityViolationException ex)
            {
                ObjectError error = new ObjectError("email","An account already exists for this email.");

                result.addError(error);

                ModelAndView modelAndView = new ModelAndView(
                        Consts.MODEL_RESISTER_PAGE);

                modelAndView.addAllObjects(result.getModel());
                modelAndView.addObject("user", user);

                return modelAndView;
            }
        }
    }

My Model:

@Entity
public class User implements Serializable
{
    /**
     * 
     */
    private static final long serialVersionUID = -5232533507244034448L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @NotEmpty
    @Size(min=2, max=15)
    private String firstname;

    @NotEmpty
    @Size(min=2, max=15)
    private String surname;

    @NotEmpty
    @Email
    private String email;

    @NotEmpty
    @Size(min=6, max=10)
    private String password;

    public Long getId()
    {
        return id;
    }

    public void setId(Long id)
    {
        this.id = id;
    }

    public String getFirstname()
    {
        return firstname;
    }

    public void setFirstname(String firstname)
    {
        this.firstname = firstname;
    }

    public String getSurname()
    {
        return surname;
    }

    public void setSurname(String surname)
    {
        this.surname = surname;
    }

    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }
}



File add.html

<form id="registrationForm" action="#" th:action="@{/registration}" th:object="${user}" method="post" class="clearfix">
    <legend>Registration</legend>

    <div class="control-group input" th:class="${#fields.hasErrors('firstname')}? 'control-group input error'">
      <input type="text" th:field="*{firstname}" placeholder="Firstname" />
      <span class="help-block" th:if="${#fields.hasErrors('firstname')}" th:errors="*{firstname}"></span>
    </div>

    <div class="control-group input" th:class="${#fields.hasErrors('surname')}? 'control-group input error'">
      <input type="text" th:field="*{surname}" placeholder="Surname" />
      <span class="help-block" th:if="${#fields.hasErrors('surname')}" th:errors="*{surname}"></span>
    </div>

    <div class="control-group input" th:class="${#fields.hasErrors('email')}? 'control-group input error'">
      <input type="text" th:field="*{email}" placeholder="Email" />
      <span class="help-block" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
    </div>

    <div class="control-group input" th:class="${#fields.hasErrors('password')}? 'control-group input error'">
        <input type="password" th:field="*{password}" placeholder="Password" />
        <span class="help-block" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>
    </div>

    <div class="clearfix">
      <input type="submit" class="btn btn-success btn-large" value="Register" />
    </div>
</form>

Answer

jelies picture jelies · Aug 24, 2012

I usually call result.rejectValue("property", "error.object"); to add errors to BindingResult. If you want to add a global object error, you could use result.reject("error.object");.

So, in your code, instead of:

ObjectError error = new ObjectError("email","An account already exists for this email.");
result.addError(error);

try with:

result.rejectValue("email", "error.user", "An account already exists for this email.");

Check the reference here.

Hope this helps.