0
0
0
38,600

Binding and Validation of Handler Parameters in Spring MVC Controllers

jorge.simao
Posted 4/29/14

Binding and Validation of Handler Parameters in Spring MVC Controllers

Abstract

mvc-params I review the different mechanisms available in Spring MVC 3.0 to inject values in the parameters of handler methods of web-tier controllers. To initialize handler method parameters from HTTP request parameters two cases are involved. For simple types the handler method parameters should be annotated with @RequestParam. For domain objects the @RequestParam annotation should be omitted and (optionally) the annotation @ModelAttribute may be used. Spring MVC maps the HTTP request parameters to properties in the domain objects. To convert the string values of HTTP parameter to typed values in Java a set of PropertyEditor are used. A few PropertyEditor are registered by default to cover primitive and some simple types. For other types additional PropertyEditor need to be registered. Validation can be performed on inject domain objects by explicit invocation of a Validator, or be done automatically by setting up JSR303 annotation @Valid.

Introduction

Data binding is the process by which the state of some domain object is initialized from some other representation of the same data in a way that is most transparent to the application. In the context of web application, data binding is used to initialize the properties of a domain object or other method parameters from the value of HTTP request parameters (from the URL query string). I show the different mechanisms available in Spring MVC 3.0 to perform data binding.

In addition to data binding, often application want domain objects to be checked for constraints on the set its properties. This is know as data validation. I also show how validation can be done in Spring MVC 3.0.

Data Binding

Data binding in Spring MVC occurs in the parameters of handler methods of the web-tier components --- Controllers.

Sample Domain Object

To illustrate how data binding is done in Spring MVC I introduce a simple domain object (POJO) for a Person. The class Person has a few data-fields and corresponding property setters/getters.

public class Person {
	private Long id;	
	private String name;
	private Double height;
	private Double weight;	
	private Date born;
	
	public Person() {
	}
	
	public Person(Long id, String name, Double height, Double weight, Date born) {
		...
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	...
}

Binding Simple Types

The class PersonController is a controller for the Person class instances. The annotation @RequestMapping("/person") is declared at the class level to specify the prefix /person for all URL that are resolved to handler methods in the controller.

@Controller
@RequestMapping("/person")
public class PersonController {
	@RequestMapping("/get")
	public String get(@RequestParam("id") Long id, Model model) {
		//TODO: get Person from DAO
		Person p = new Person(id, "Joe", 30.0, 110.0, new Date());
 		model.addAttribute(p);
		return "person/show";
	}
}

The class defines (for now) a single handler method get() that maps the URL suffix /get. This method takes a parameter with type Long for the identifier of the instance of Person to retreive. The annotation @RequestParam is used in the method parameter to request its value to be bound to an HTTL request parameter named id.

(Note that the same name is being used for the Java method parameter and for the name of the HTTP request parameter --- as specified in the value of annotation @RequestParam. This is really circumstantial to this example, although is a common practice. We could have used @RequestParam("person_id") as well. In fact, the name of Java method parameters is usually available at run-time, and this is the reason why the @RequestParam is required).

URLs of the form below will map to the handler method PersonController.get():

http://localhost:8080/tsmvc/person/get?id=1

Binding Domain Objec

To perform data-binding of domain objects in handler method parameters the annotation @RequestParam should not be used. For a domain object handler parameter an instance is automatically created by Spring MVC. The properties of this domain object are initialized from whatever HTTP request parameters are available in the URL query string. Spring MVC uses Java reflection to learn about the names of properties of the domain object. The names of the HTTP request parameters should match the name of these properties.

Below, the PersonController is modified to have one additional handler method: PersonController.create(), that takes a single parameter of type Person. A Person instance is automatically created by Spring MVC on calling this handler method. The property setters (or direct field access) is used to initialize the object with whatever parameters are available in the URL query string.

@Controller
@RequestMapping("/person")
public class PersonController {
	...		
	@RequestMapping("/create")
	public String create(Person p) {
		//TODO: add Person to DAO
		return "person/show";
	}
}

Note that no Model parameter is declared in the signature of PersonController.create(). The Model is however created implicitly by Spring MVC. The created and initialized Person instance is also stored in the Model as a variable named person. This means that the views such as person/show can safely refer to such model variable, as exemplified below:

<?xml version="1.0" encoding="UTF-8" ?>
<%@ page %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Person</title>
</head>
<body>

<h1>Person</h1>

<p>ID: ${person.id}</p>
<p>Name: ${person.name}</p>
<p>Height: ${person.height}</p>
<p>Weight: ${person.weight}</p>
<p>Born: ${person.born}</p>

</body>
</html>

If we want to have control on the naming by which the created domain object is stored in the (implicit) model, then the annotation @ModelAttribute can be used. This is shown below:

@Controller
@RequestMapping("/person")
public class PersonController {
	...		
	@RequestMapping("/create")
	public String create(@ModelAttribute("p") Person p) {
		//TODO: add Person to DAO
		return "person/show";
	}
}

The Person instance is stored in the model with name p. So the views such as person/show can be made less verbose.

...
<p>ID: ${p.id}</p>
<p>Name: ${p.name}</p>
<p>Height: ${p.height}</p>
<p>Weight: ${p.weight}</p>
<p>Born: ${p.born}</p>
...

Binding Errors

To actually initialize the Person instance the URL should have the right names and values for the request parameters. For example:

http://localhost:8080/tsmvc/person/create?name=joe&height=120&weight=30

This URL maps to the PersonController.create() handler method, and a Person instance is created and initialized with properties name="joe", height=120.0, weight=30.0.

The mapping above works fine because all these properties are of primitive or simple Java types. For these types, Spring MVC knows automatically how to convert the HTTP request parameter string values to Java typed values. This is done internally by registering a set of PropertyEditor that can handle these types.

For other types, however, there may not be a PropertyEditors registered by default. This is the case of type java.util.Date which is the type of the property Person.born. This means that an URL such as the one below produces a binding error:

http://localhost:8080/tsmvc/person/create?name=joe&height=120&weight=30&born=2010-10-10

Binding errors can be learn about in an handler method my declaring a BindingResult parameter. Defining such parameter also prevents an exception to be thrown by Spring MVC when there are binding errors. This is shown below:

@RequestMapping("/person")
public class PersonController {
    ...
	@RequestMapping("/create")
	public String create(Person p, BindingResult errors) {
		if (errors.hasErrors()) {
			...
		}
		return "person/show";
	}
}

Registering Property Editors

To handle non-primitive or simple Java types in data binding, additional PropertyEditor should be defined and/or registered. A convenient place to do this is in a controller method with annotation @InitBinder.

Below, I how a PropertyEditor for java.util.Date can be registered:

@RequestMapping("/person")
public class PersonController {
	
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		dateFormat.setLenient(false);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
	}
	...	
}

This should make URLs with parameter named born work fine. The property Person.born will be initialized with a java.util.Date object, provided that the value of the HTTP request parameter has the right format.

Rich Domain Object

Data binding in SpringMVC also works with rich domain object. Recursion is applied so that structured properties are also initialized. Suppose the class Person is modified to have a property of type Address defined as show below:

public class Address {
	private String street;
	private String city;
	private String zip;
	private String country;
	
	public Address() {
	}

	public Address(String street, String zip, String city, String country) {
		...
	}

	public String getStreet() {
		return street;
	}

	public void setStreet(String street) {
		this.street = street;
	}
	...		
}
public class Person {
	private Long id;	
	private String name;
	private Double height;
	private Double weight;	
	private Date born;
	private Address address;
	
	...
}	

Data binding works also for property Person.address and its own properties. Spring MVC creates an instance of Address and makes it the value of property address of the Person instance, provided that there is a HTTP request parameter with prefix address.. The URL below produces the this result (protocol and host name ommited):

.../person/create?name=joe&height=120&weight=30&born=2010-10-10&address.country=PT

The HTTP request parameter address.country binds to property Person.address.country.

Validation

In Java there is a standard for validation: JSR303 --- Bean Validation. This defines the API to define declarative constraints on field or properties using annotations, and a programmatic API.

Below, I show how JSR303 validation annotations can be declared in our sample domain class Person:

public class Person {
	private Long id;	
	@NotNull
	@Size(min=2)
	private String name;
	@NotNull	
	private Double height;
	@NotNull
	private Double weight;	
	@NotNull
	@Past
	private Date born;

	private Address address;
	...
}

Spring MVC allow validation to be performed on domain objects injected in handler method parameters. While validation can be done programmatic by the application (e.g. in the Controller or the DAO), starting with Spring MVC 3.0 validation can be performed declaratively with annotation @javax.validation.Valid in the handler method parameter.

Below, I show the PersonController.create() handler method requesting validation to the Person domain object.

@RequestMapping("/person")
public class PersonController {
    ...
	@RequestMapping("/create")
	public String create(@Valid Person p, BindingResult errors) {
		if (errors.hasErrors()) {
			List<FieldError> lerr = errors.getFieldErrors();
			for (FieldError err: lerr) {
				System.err.println(err);		
			}
		}
		...
	}
}

The error handling code also show how to get a hold of the binding and/or validation errors.

To JSR303 work in SpringMVC a validation provider need to be available in the classpath. In web applications this would typically be a JAR file under directory /WEB-INF/lib. The validation libraries for JSR303 always include the API of javax.validation.*. If using Hibernate validator as provider, then the respective JAR should also be available. You can check the resources listed below for this.

Discussion and Comparison to Other Approaches

Discussion...

Summary and Conclusions

I have shown how...

Summary and Conclusions

Data binding and validation can be done in Spring MVC 3.0 in a very effective way. The parameters of handler methods are automatically injected with the values taken from HTTP parameters. Primitive, wrappers and simple data types such as java.util.String should be annotated with @RequestParam. Domain objects are automatically created and have their properties initialized from HTTP parameters with the same names as the property names. Properties with support domain types are initialized using HTTP parameters names like address.country. JSR303 validation can be requested by declaring annotation @javax.validation.Valid.

Exercises to the Reader

  • Setup a dynamic web project that uses Spring MVC. Copy&past and assemble the code fragment show earlier to run the PersonController.
  • Extend the implementation of PersonController to implement additional CRUD operations for the class Person, including: list(), delete(), and update(). Implement JSP views to be used by the handler methods.
  • Create a DAO interface for Person (and for Address) and call the DAO from the PersonController. You may consider an in-memory DAO implementation, and/or a persistence DAO with JDBC or Hibernate/JPA implementation. Consider using dependency injection and @Autowired in the PersonController to get a reference to the DAO.
  • Add some JSR303 validation constraints to the Address class. Check if constraint violations are detected using some hand-made URLs.

Resources

References and Documentation

Software Projects

Exercises to the Reader

Below, I present some suggestions to possible extensions to the approach presented in this article, left as exercises to the reader:

  • Exercise 1 - Consider extending the approach, as follow:

2 Comments

4/7/15

55

4/7/15

66

Post Your Comment

Login (or Register)
Contribute Feedback