jueves, 25 de agosto de 2011

Spring MVC II - Form Processing & Validations

Este tutorial pretende explicar el procesamiento de formularios mediante Spring MVC de forma muy simple.
Antes de leer este tutorial, es necesario leer http://java-all-frameworks.blogspot.com/2011/08/spring-mvc-helloworld-en-5-minutos.html para poder entender el codigo que mostraremos a continuación.

El controlador va a tener dos roles:
  • Va a inicializar el form
  • Va a procesar el form



Front Controller

El controlador va a extender de SimpleFormController (Clase util para inicializar y procesar formularios mediante Spring MVC)

Command

Por otra parte, vamos a tener un objeto Command que actuara como el ActionForm de Struts pero con la diferencia que no va a tener que extender de nada. Lo que hace posible que se trabaje directamente con la clase del modelo (Ej, vamos a trabajar con la clase Car)

SpringHelloWorld-servlet.xml

<bean name="/new_car.html" class="springmvc.web.CarNewController">
<property name="commandClass" value="springmvc.model.Car" />
<property name="formView" value="car_new" />
<property name="successView" value="car_list.html"/>
<property name="validator">
        <bean class="springmvc.validator.CarValidator"/>
        </property>
</bean>

Mediante la property commandClass vamos a definir el form/modelo/dto (en este caso vamos a utilizar directamente el objeto del modelo (Car).
Mediante esto, vamos a poder utilizar directamente los campos (properties) del objeto Car en la jsp para que se efectué automáticamente el binding entre lo que el usuario va a ingresar por pantalla (jsp) y como va a llegar estos valores al controller.

Mediante la property formView vamos a definir cual sera la JSP que se mostrara como formulario.

Mediante la property successView vamos a definir cual sera la JSP que se mostrara cuando presionamos submit en el formulario.

Mediante la property validator vamos a definir cual sera la clase que interceptara el submit y efectuara las validaciones pertinentes. En este caso, sera CarValidator.

CarNewController.java

package springmvc.web;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;

import springmvc.model.Brand;
import springmvc.model.Car;

/**
 * Struts controllers extend Action whereas Spring controllers extend/implement a 
 * Controller class/interface. 
 * There are many of them you can choose from. The most basic is Controller, 
 * When it comes to process a form, SimpleFormController is generally used.
 * 
 * A form controller has two roles: initialize the form's initial values 
 * and process/persist the car 
 */
public class CarNewController  extends SimpleFormController{
List<Brand> brandList = new ArrayList<Brand>();
List<Car> carList = new ArrayList<Car>();

/**
*  Initialize the Command used to init the form
*  
*  A Command object is used to store the form values: it's equivalent to a 
*  Struts Action Form. However, it doesn't have to extend nor 
         *  implement anything. 
*  So it's even possible to directly use the model class! 
         * (Car in our example)
*  
*  Se va a llamar la primera vez q se ingresa al form como 
         * cuando presionamos submit.
*/
@Override
protected Object formBackingObject(HttpServletRequest request) 
                                             throws Exception {
System.out.println("formBackingObject()");
// Inicializamos los valores que queremos que aparezcan 
                // por primera vez al ingresar al formulario
Car defaultCar = new Car();
defaultCar.setModel("new model");
defaultCar.setPrice(new BigDecimal(15000));
return defaultCar;
}

/**
*  Set the view attributes (using a Map)
*  These are called after:
*   a. initBinder()
*   b. onSubmit()
*  
*  Se va a llamar solamente la primera vez q si ingrese al formulario.
*/
@Override
protected Map referenceData(HttpServletRequest request) throws Exception {
System.out.println("referenceData()");
// Inicializamos los valores que vamos a mostrar en 
                // cada campo del formulario
Map<Object, Object> dataMap = new HashMap<Object, Object>();
dataMap.put("brandList", getBrandList());
return dataMap;
}

/**
* This method prevent Spring to do some bindings and so them by 
         * ourselves if needed. 
* Here we used the brand id parameter to set the actual Brand
* Se va a llamar la primera vez q se ingresa al form como cuando 
         * presionamos submit.
*/
@Override
protected void initBinder(HttpServletRequest request, 
                                 ServletRequestDataBinder binder) 
                                            throws Exception {
System.out.println("initBinder()");
binder.setDisallowedFields(new String[] {"brand"});
String brand = request.getParameter("brand");
System.out.println("brand input: " + brand);
String model = request.getParameter("model");
System.out.println("model input: " + model);
String price = request.getParameter("price");
System.out.println("price input: " + price);

Car car = (Car)binder.getTarget();
// set car's brand from request parameter brand id
Long brandId = null;
try {
brandId = Long.parseLong(brand);
} catch (Exception e) {}
if (brandId != null) {
Brand brandSelected = getBrandById(brandId);
car.setBrand(brandSelected);
}    
}

/**
* This method has the main code. In this case, we used the command object, 
* which is a Car, to create a new Car.
* Solo se va a llamar cuando presionamos submit.
*/
@Override
public ModelAndView onSubmit(Object command) throws ServletException {
System.out.println("onSubmit() INICIO");
Car car = (Car)command;
System.out.println("******* Nuevo Auto ********"); 
System.out.println("CAR " + car);
System.out.println("******* Nuevo Auto ********");
carList.add(car);
System.out.println("******* Listado de Autos INICIO ********");
for (Car c : carList) {
System.out.println("Car " + c);
}
System.out.println("******* Listado de Autos FIN ********");
System.out.println("onSubmit() FIN");
return new ModelAndView(new RedirectView(getSuccessView()));
}
/********************************************/
/*********** private methods ***************/
/********************************************/
private List<Brand> getBrandList(){
brandList.clear();
Brand b1 = new Brand();
b1.setCountry("Argentina");
b1.setId(10l);
b1.setName("Brand 1");
Brand b2 = new Brand();
b2.setCountry("Brasil");
b2.setId(20l);
b2.setName("Brand 2");
brandList.add(b1);
brandList.add(b2);
return brandList;
}
private Brand getBrandById(long id){

for (Brand brand : brandList) {
if (brand.getId().equals(id)){
return brand;
}
}
return null;
}
}


CarValidator.java

package springmvc.validator;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import springmvc.model.Car;

public class CarValidator implements Validator {

public void validate(Object obj, Errors errors) {

Car car = (Car) obj;

ValidationUtils.rejectIfEmptyOrWhitespace(
                       errors, "model", "field.required", "Required field");
ValidationUtils.rejectIfEmptyOrWhitespace(
                       errors, "price", "field.required", "Required field");

if ( !errors.hasFieldErrors("price")) {
if (car.getPrice().intValue() == 0)
errors.rejectValue("price", "not_zero", 
                                        "Can't be free!");
}
}

/**
* This method declare classes supported by this validator
*/
@Override
public boolean supports(Class aClass) {

return Car.class.equals(aClass);
}

}

car_new.jsp


<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>


<head>
  <title>New Sponsor</title>
  <style>
    .error {
    color: red;
    }
  </style>  
</head>


<body>
<h1>New Car</h1>

<form:form method="post">

Brand<br />
<form:select path="brand">
  <form:options items="${brandList}" itemLabel="name" itemValue="id" />
</form:select>
<br /><br />

Model <form:errors path="model" cssClass="error"/><br />
<form:input path="model"/><br /><br />

Price <form:errors path="price" cssClass="error"/><br />
<form:input path="price"/><br /><br />

<input type="submit" value="Submit">

</form:form>
</body>
</html>


Url del proyecto

Proyecto completo en el eclipse


Descargar ejemplo completo

Para descargar el código completo, por favor haga clic aqui

No hay comentarios:

Publicar un comentario