ZetCode

Spring BindingResult

last modified October 18, 2023

Spring BindingResult tutorial shows how to use BindingResult to get the result of a validation.

Spring is a popular Java application framework for creating enterprise applications.

BindingResult

BindingResult holds the result of a validation and binding and contains errors that may have occurred. The BindingResult must come right after the model object that is validated or else Spring fails to validate the object and throws an exception.

Spring BindingResult example

The following application validates a user form and uses BindingResult to store the validation results.

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           ├───config
│   │           │       MyWebInitializer.java
│   │           │       WebConfig.java
│   │           ├───controller
│   │           │       MyController.java
│   │           └───form
│   │                   UserForm.java
│   └───resources
│       └───templates
│               form.html
│               showInfo.html
└───test
    └───java

This is the project structure.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zetcode</groupId>
    <artifactId>bindingresultex</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <spring-version>5.3.23</spring-version>
        <thymeleaf-version>3.0.15.RELEASE</thymeleaf-version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.1</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.2.5.Final</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>${thymeleaf-version}</version>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>${thymeleaf-version}</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>

            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.49.v20220914</version>
            </plugin>

        </plugins>
    </build>
</project>

In the pom.xml file, we have the project dependencies.

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.5.Final</version>
</dependency>

We use hibernate-validator for validation.

com/zetcode/config/MyWebInitializer.java
package com.zetcode.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

@Configuration
public class MyWebInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {

        return new Class[]{WebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {

        return new String[]{"/"};
    }
}

MyWebInitializer initializes the Spring web application. It contains one configuration class: WebConfig.

com/zetcode/config/WebConfig.java
package com.zetcode.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.zetcode"})
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public SpringResourceTemplateResolver templateResolver() {

        var templateResolver = new SpringResourceTemplateResolver();

        templateResolver.setApplicationContext(applicationContext);
        templateResolver.setPrefix("classpath:/templates/");
        templateResolver.setSuffix(".html");

        return templateResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {

        var templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.setEnableSpringELCompiler(true);

        return templateEngine;
    }

    @Bean
    public ViewResolver viewResolver() {

        var resolver = new ThymeleafViewResolver();
        var registry = new ViewResolverRegistry(null, applicationContext);

        resolver.setTemplateEngine(templateEngine());
        registry.viewResolver(resolver);

        return resolver;
    }
}

The WebConfig configures the Thymeleaf template engine. The Thymeleaf template files are located in the templates subdirectory on the classpath.

com/zetcode/form/UserForm.java
package com.zetcode.form;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

public class UserForm {

    @NotBlank
    @Size(min = 2)
    private String name;

    @NotBlank
    @Email
    private String email;

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

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

This is a form bean. It contains some validation annotations.

@NotBlank
@Size(min = 2)
private String name;

The name attribute must not be blank and must have at least 2 characters.

@NotBlank
@Email
private String email;

The email attribute must not be blank and must be a well-formed email.

com/zetcode/controller/MyController.java
package com.zetcode.controller;

import com.zetcode.form.UserForm;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.validation.Valid;

@Controller
public class MyController {

    @GetMapping(value = "/")
    public String form(UserForm userForm) {

        return "form";
    }

    @PostMapping("/")
    public String checkForm(@Valid UserForm userForm, BindingResult bindingResult,
                            RedirectAttributes atts) {

        if (bindingResult.hasErrors()) {
            return "form";
        }

        atts.addAttribute("name", userForm.getName());
        atts.addAttribute("email", userForm.getEmail());

        return "redirect:/showInfo";
    }

    @GetMapping("/showInfo")
    public String showInfo(@ModelAttribute("name") String name,
                           @ModelAttribute("email") String email) {

        return "showInfo";
    }
}

MyController contains mappings of request paths to handler methods.

@GetMapping(value = "/")
public String form(UserForm userForm) {

    return "form";
}

The home page returns a view that contains a form. The UserForm bean is backing a form. It is going to be populated with data from the form.

@PostMapping("/")
public String checkForm(@Valid UserForm userForm, BindingResult bindingResult,
                        RedirectAttributes atts) {
...

We validate the UserForm bean with @Valid. The validation results are stored in BindingResult.

if (bindingResult.hasErrors()) {
    return "form";
}

If the binding result contains errors, we return to the form.

atts.addAttribute("name", userForm.getName());
atts.addAttribute("email", userForm.getEmail());

return "redirect:/showInfo";

Adhering to the redirect after post pattern, we redirect to the showInfo view after successful validation. In order not to lose the inputs, we store them in RedirectAttributes.

@GetMapping("/showInfo")
public String showInfo(@ModelAttribute("name") String name,
                        @ModelAttribute("email") String email) {

    return "showInfo";
}

The @ModelAttribute takes the request attributes nad puts them into the model object, which is then sent to the showInfo view.

resources/templates/form.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>User form</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
</head>
<body>

<section class="ui container">

    <form action="#" class="ui form" th:action="@{/}" th:object="${userForm}" method="post">
        <div class="field">
            <label>Name:</label>
            <input type="text" th:field="*{name}">
            <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</span>
        </div>

        <div class="field">

            <label>Email:</label>

            <input type="text" th:field="*{email}">
            <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</span>
        </div>

        <button class="ui button" type="submit">Submit</button>

    </form>

</section>

</body>
</html>

The root page contains the form.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">

The form is styled using Semantic UI.

<form action="#" class="ui form" th:action="@{/}" th:object="${userForm}" method="post">

The th:object refers to the user form bean. This is not a class name, but a Spring bean name; therefore it is in lowercase.

<input type="text" th:field="*{name}">

The input is mapped to the name attribute of the userForm.

<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</span>

This line displays possible validation errors.

resources/templates/showInfo.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Show info</title>
</head>
<body>

<p>
    Successfully added user <span th:text="${name}" th:remove="tag"></span> with email
    <span th:text="${email}" th:remove="tag"></span>
</p>

</body>
</html>

This view shows the entered information.

In this article we have used BindingResult when validating a form.

Author

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.

List all Spring tutorials.