How to handle HTML forms in Spring

Spring MVC is a part of Spring Framework which main purpose is to simplify development of web applications. In this article I would like to explain how to handle HTTP requests, render the web page and process simple HTML forms in Spring.

To show this we will build a simple application which displays first name, second name and age of already added persons at the top of the page and which additionally displays and handles HTML form for adding more persons. The main window of the application will look like this:
springmvcforms

Servlet configuration

Like most Java web frameworks Spring is based on the servlet technology and therefore it requires special definitions in WEB-INF/web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    
    <servlet>
        <servlet-name>springmvcforms</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>springmvcforms</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

We specify there that we want to use class org.springframework.web.servlet.DispatcherServlet as our main servlet and we map it to handle any requests arriving to our application.

Spring configuration

In Spring we have to additionally provide configuration for our dispatcher servlet named springmvcforms. We can do so by placing an XML file named springmvcforms-servlet.xml in the same directory as web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
 
    <context:component-scan base-package="com.example.springmvcforms" />
    <mvc:resources mapping="/resources/**" location="/resources/" />
    <mvc:annotation-driven />
             
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

By default the name of the configuration file is a concatenation of the name of the servlet and -servlet.xml suffix. It possible to change its name but we want to stick to the default values because we want to limit the size of the configuration.

We specify two important tags <context:component-scan> and <mvc:annotation-driven> to inform Spring that we want to use annotations for our beans located under given base package (former) and for our web controllers (later). Without annotations we would have to add every bean and web controller definition to this file which will greatly increase its size.

We also inform Spring that we want to hold our static resources like images, CSS files and scripts under /resources/ directory of our application. By adding mapping attribute we tell Spring that whenever any HTTP request arrives for URL starting with /resources/ (including its children and all other descendants), we want to return one of our resources. Defining static resources improves performance because Spring will serve the resources directly without any checking or processing.

At the end of the configuration file we define our view resolver. Generally, view resolver is responsible for mapping the logical view name (as returned by web controllers) into the actual JSP, FreeMarker, Tiles or any other view file and then for converting it into appropriate HTML file which will be served to the web browser.

We use InternalResourceViewResolver which locates the actual view file in WAR by adding given prefix and suffix to the logical view name. Because we also want to use JSP with JSTL we configure our own view class.

Web controller

In our application we have to tell Spring which URLs we can handle and how. We can do so by creating a web controller class:

package com.example.springmvcforms;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(value = "/personlist")
public class PersonListController {
    
    @Autowired
    private PersonList list;
    
    @RequestMapping(method = RequestMethod.POST)
    public String add(@ModelAttribute(value = "newPerson") Person newPerson, BindingResult result, Model model) {
        if (result.hasErrors())
            return "personlist";
        
        list.addPerson(newPerson);
        return "redirect:/personlist";
    }
    
    @RequestMapping(method = RequestMethod.GET)
    public String list(Model model) {
        model.addAttribute("list", list.getAll());
        model.addAttribute("newPerson", new Person());
        return "personlist";
    }
}

Annotations @Controller and @RequestMapping at class level inform Spring that this class is a web controller and will serve requests for /personlist relative URL.

We don’t have to add this class to any configuration file or register it in any way because Spring will automatically scan all classes within base package specified in XML configuration file and register them.

Gathering data to display

Let’s first take a look at method list which will be called whenever HTTP GET request arrives for /personlist relative URL. This method takes an instance of a model which it fills with two attributes. One is the list of all already added persons and which will be used for display it on the web page. The second is a new person which will be bound to the HTML form and which properties will be attached to appropriate fields of the form.

Person class is a very simple POJO class containing only three properties:

package com.example.springmvcforms;

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 3297423984732894L;
    
    private String firstName;
    private String lastName;
    private Integer age;

    public Person() {
        firstName = "";
        lastName = "";
        age = null;
    }
    
    public String getFirstName() {
        return firstName;
    }
    
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

Displaying data

In the list method we return the logical name of the view to display (personlist) which will be mapped to a JSP file at location /WEB-INF/views/personlist.jsp in our WAR:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Person List</title>
        <link href="<s:url value='/resources/css/styles.css'/>" rel="stylesheet" type="text/css"/>
    </head>
    <body>
        <p>
            <c:forEach var="person" items="${list}">
                <span class="box-with-border"><c:out value="${person.firstName} ${person.lastName} (${person.age})" /></span>
            </c:forEach>
        </p>
        
        <sf:form method="POST" modelAttribute="newPerson" cssClass="personForm">
            <table>
                <tr>
                    <td><sf:label path="firstName">First name:</sf:label></td>
                    <td><sf:input path="firstName"/></td>
                </tr>
                <tr>
                    <td><sf:label path="lastName">Last name:</sf:label></td>
                    <td><sf:input path="lastName"/></td>
                </tr>
                <tr>
                    <td><sf:label path="age">Age:</sf:label></td>
                    <td><sf:input path="age" /></td>
                </tr>
                <tr><td></td><td><input type="submit" value="Add" /></td></tr>
            </table>
        </sf:form>
        
    </body>
</html>

In the <c:forEach> element we iterate over all persons using the list attribute which we have previously added to the model in our controller class. Then we display our HTML form and we bind it to the newPerson attribute from model. We don’t define action attribute for the form so the form data will be sent back to the same URL from which it was received and we use POST method so there is no conflict between displaying the persons and creating a new person.

As you could notice we use special Spring-forms tag library which provides path attribute. This attribute attaches the properties of our model attribute newPerson to the appropriate HTML elements.

Processing the HTML forms

If we submit the form by clicking Add button, the form data will be sent to the server to /personlist relative URL using the POST method. Spring will recognize this request and will call add method of our web controller with appropriate arguments:

@RequestMapping(method = RequestMethod.POST)
public String add(@ModelAttribute(value = "newPerson") Person newPerson, BindingResult result, Model model) {
    if (result.hasErrors())
        return "personlist";
        
    list.addPerson(newPerson);
    return "redirect:/personlist";
}

The arguments contain the reference to our model attribute newPerson with properties set based on the data submitted in the form, the reference to BindingResults which contains form validation errors (if any) and the model which may be used to render the response. We annotated the attribute newPerson with @ModelAttribute to explicitly state which model attribute we mean.

In the add method we check if there are validation errors (e.g. age field in the HTML form could not be converted into a number). If the validation went fine, we add the person to the list and we redirect the user to our main page. Otherwise, we return the user back to the form so that he/she has a chance to fix the problems.

Handling the home page

If a user enters the home page of our application (e.g. http://localhost:8080/springmvcforms/), he/she will receive HTTP 400 error which is a bit discouraging. We can change it by creating a new web controller to handle requests for our home page:

package com.example.springmvcforms;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HomePageController {

    @RequestMapping("/")
    String showHome() {
        return "redirect:/personlist";
    }
}

This is the easiest controller so far and it redirects the user to our /personlist web page. Also note that we don’t pass any arguments to our showHome method because we just don’t use them.

Conclusion

As you could see displaying web pages and handling forms in Spring is quite simple and straightforward. Although the configuration in XML files may seem overwhelming, it is done only once and usually does not need to changed when adding more controllers or views.

Because I wanted to keep the example quite simple and easy to deploy, I did not use database to store the list of persons but instead I used a bean with a session scope.

The complete code for the example can be found at GitHub.

About Robert Piasecki

Husband and father, Java software developer, Linux and open-source fan.
This entry was posted in Java, Spring and tagged , , , , , , . Bookmark the permalink.

6 Responses to How to handle HTML forms in Spring

  1. Pingback: Using JPA and JTA with Spring | softwarecave

  2. Pingback: Hibernate with Spring | softwarecave

  3. Jami says:

    If some one desires to be updated with newest technologies after that he must be go to see
    this site and be up to date everyday.

  4. Pingback: web.xml | Dicas de Java

  5. marc says:

    like your mother

  6. DonnieAcums says:

    medical home remedies https://reductil.clubeo.com tuberculosis herbal treatment

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.