Spring Form Validation

February 10, 2014 Colin Shield

This post is aimed at helping rails developers who are familiar with the patterns around Active Record model validations and simple form and are moving to the Spring world.

We can start with a simple Spring Model


package com.springapp.mvc;

import javax.persistence.*;

@Entity(name = "account")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;
    private String email;

    // getters and setters ...
}

In rails we’d add validations to our User model. The model would have errors added to it at save time and an appropriate form would display those validation errors. Adding some simple validation to a comparable Active Record model would look like this:


class User < ActiveRecord::Base
  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :email, presence: true, email: true
end

In order to add the same validations to our Spring model, we have to first add some jars to our pom.xml:


<dependency>
  <groupId>javax.validation</groupId>
  <artifactId>validation-api</artifactId>
  <version>1.1.0.Final</version>
</dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>5.0.3.Final</version>
</dependency>

In Spring, annotations are added the model to achieve the same result. The errors are added to the model and can be displayed similarly on the form.


import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

import javax.persistence.*;
@Entity(name = "account")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @NotEmpty
    private String firstName;
    @NotEmpty
    private String lastName;
    @NotEmpty @Email
    private String email;
}

The validation failures can be passed through to a form and their default error strings displayed. Here’s a controller and form snippet to demonstrate:


package com.springapp.mvc;

import javax.validation.Valid;

@Controller
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String addUser(@Valid @ModelAttribute("user") User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "users";
        }
        userRepository.save(user);
        return "redirect:/";
    }
}


<div class="container">
    <div class="row">
        <div class="span8 offset2">
            <h1>Users</h1>
            <form:form method="post" action="add" commandName="user" class="form-horizontal">
            <div class="control-group">
                <form:label cssClass="control-label" path="firstName">First Name:</form:label>
                <div class="controls">
                    <form:input path="firstName"/>
                    <form:errors path="firstName" cssClass="error" />
                </div>
            </div>
            <div class="control-group">
                <form:label cssClass="control-label" path="lastName">Last Name:</form:label>
                <div class="controls">
                    <form:input path="lastName"/>
                    <form:errors path="lastName" cssClass="error" />
                </div>
            </div>
            <div class="control-group">
                <form:label cssClass="control-label" path="email">Email:</form:label>
                <div class="controls">
                    <form:input path="email"/>
                    <form:errors path="email" cssClass="error" />
                </div>
            </div>
            <div class="control-group">
                <div class="controls">
                    <input type="submit" value="Add User" class="btn"/>
                    </form:form>
                </div>
            </div>
        </div>
    </div>
</div>

The errors are displayed as the form as below:
Screen Shot 2014-02-09 at 4.08.00 PM

Finally this being Pivotal Labs I obviously test drove my work. I started with some model level tests that while fun to write I don’t think I’ll be repeating in the future.


package com.springapp.mvc;

public class UserTest {

    private static Validator validator;
    private User user;

    @Before
    public void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
        user = new User();
        user.setFirstName("Joe");
        user.setLastName("Bloggs");
        user.setEmail("email@example.com");
    }

    @Test
    public void firstNameIsEmpty() {
        user.setFirstName("");
        Set<constraintViolation> constraintViolations =
                validator.validate( user );

        assertEquals( 1, constraintViolations.size() );
        assertEquals(
                "may not be empty",
                constraintViolations.iterator().next().getMessage()
        );
    }

    @Test
    public void lastNameIsEmpty() {
        user.setLastName("");
        Set<constraintViolation> constraintViolations =
                validator.validate( user );

        assertEquals( 1, constraintViolations.size() );
        assertEquals(
                "may not be empty",
                constraintViolations.iterator().next().getMessage()
        );
    }

    @Test
    public void emailIsEmpty() {
        user.setEmail("");
        Set<constraintViolation> constraintViolations =
                validator.validate( user );

        assertEquals( 1, constraintViolations.size() );
        assertEquals(
                "may not be empty",
                constraintViolations.iterator().next().getMessage()
        );
    }

    @Test
    public void emailIsInvalid() {
        user.setEmail("Not An Email");
        Set<constraintViolation> constraintViolations =
                validator.validate( user );

        assertEquals( 1, constraintViolations.size() );
        assertEquals(
                "not a well-formed email address",
                constraintViolations.iterator().next().getMessage()
        );
    }

}

I much preferred the integration level test that posted the form and looked at http result codes.


package com.springapp.mvc;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml")
public class AppTest {
private MockMvc mockMvc;

@SuppressWarnings("SpringJavaAutowiringInspection")
@Autowired
protected WebApplicationContext wac;

@Before
public void setup() {
this.mockMvc = webAppContextSetup(this.wac).build();
}

@Test
public void postForm() throws Exception {
mockMvc.perform(post("/add")
.param("firstName", "Joe")
.param("lastName", "Bloggs")
.param("email", "email@example.com")
).andExpect(status().isFound());
}


</pre

The full source can be found on github.

About the Author

Biography

Previous
Chromecast: With this Great Threat Comes Great Opportunity
Chromecast: With this Great Threat Comes Great Opportunity

How many awesome YouTube videos have been ruined by the five or so people crowding around a tiny laptop scr...

Next
Spring 4 MVC with Scala
Spring 4 MVC with Scala

Why? In my previous job at The Guardian I used Scala on various projects and enjoyed it. We employed variou...

×

Subscribe to our Newsletter

!
Thank you!
Error - something went wrong!