ZetCode

Spring Boot Flash attributes

last modified July 24, 2023

In this article we show how to create flash messages in Spring Boot applications.

Spring is a popular Java application framework and Spring Boot is an evolution of Spring that helps create stand-alone, production-grade Spring based applications easily.

Flash messages are temporary data used for user notifications or storing form input. They are stored in a session and vanish as soon as they are retrieved.

Flash messages in Spring are created as flash attributes using RedirectAttributes's addFlashAttribute. They are used in conjunction with RedirectView.

Spring Boot Flash attributes example

In the following application, we create flash attributes for notifications and for remembering form input values. We have a form with two inputs. If the input values do not meet the validation criteria, the application redirects to the form page and shows error messages; these messages are sent as flash attributes.

In addition, the correct values of a form are remembered.

build.gradle
...
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           └───controller
│   │                   MyController.java
│   │
│   └───resources
│       └───templates
│               index.html
│               showMessage.html
└───test
    └───java

This is the project structure of the Spring application.

build.gradle
plugins {
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final'
}

This is the Gradle build file. We use spring-boot-starter-thymeleaf for templating with Thymeleaf and hibernate-validator for validation of form data.

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

import jakarta.validation.ConstraintViolationException;
import jakarta.validation.constraints.Size;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
import org.thymeleaf.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@Controller
@Validated
public class MyController {

    @RequestMapping("/")
    public String index(Model model) {

        return "index";
    }

    @RequestMapping("/message")
    public ModelAndView message(@RequestParam @Size(min = 2, max = 255) String name,
                                @RequestParam @Size(min = 2, max = 255) String occupation) {

        var msg = String.format("%s is a %s", name, occupation);

        Map<String, Object> params = new HashMap<>();
        params.put("message", msg);

        return new ModelAndView("showMessage", params);

    }

    @ExceptionHandler(ConstraintViolationException.class)
    public RedirectView handleError(ConstraintViolationException ex,
                                    WebRequest request,
                                    RedirectAttributes atts) {

        var name = request.getParameter("name");
        var occupation = request.getParameter("occupation");

        var errorMessages = new ArrayList<String>();
        var violations = ex.getConstraintViolations();

        violations.forEach(violation -> {
            var error = String.format("%s: %s", violation.getPropertyPath(),
                    violation.getMessage());
            errorMessages.add(error);
        });

        if (!StringUtils.isEmptyOrWhitespace(name)) {
            atts.addFlashAttribute("name", name);
        }

        if (!StringUtils.isEmptyOrWhitespace(occupation)) {

            atts.addFlashAttribute("occupation", occupation);
        }

        atts.addFlashAttribute("messages", errorMessages);

        return new RedirectView("/");
    }
}

This is MyController. It responds to the request from the client. It finds out the current date and time and resolves the processing to the showMessage.ftl template, passing it data.

@Controller
@Validated
public class MyController {

The @Validated annotation validates annotated request parameters. In our case, we use two @Size annotations.

@RequestMapping("/")
public String index(Model model) {

    return "index";
}

The root page returns the index view, which sends a form to the client.

@RequestMapping("/message")
public ModelAndView message(@RequestParam @Size(min = 2, max = 255) String name,
                            @RequestParam @Size(min = 2, max = 255) String occupation) {

    var msg = String.format("%s is a %s", name, occupation);

    Map<String, Object> params = new HashMap<>();
    params.put("message", msg);

    return new ModelAndView("showMessage", params);
}

This action responds to the form submission. The two input parameters, name and occupation, are annotated with @Size. If all goes OK, a message is built from the parameters and is sent to the client with the showMessage view.

@ExceptionHandler(ConstraintViolationException.class)
public RedirectView handleError(ConstraintViolationException ex,
                                WebRequest request,
                                RedirectAttributes atts) {

If the input parameters fail to validate, a ConstraintViolationException is thrown. We react to the exception in the provided exception handler.

var name = request.getParameter("name");
var occupation = request.getParameter("occupation");

We get the request parameters. They are used to keep the correct form input values.

var errorMessages = new ArrayList<String>();
var violations = ex.getConstraintViolations();

violations.forEach(violation -> {
    var error = String.format("%s: %s", violation.getPropertyPath(),
            violation.getMessage());
    errorMessages.add(error);
});

We get the constraint violations and build a list of error messages. The error messages are going to be shown in the index form page above the form.

if (!StringUtils.isEmptyOrWhitespace(name)) {
    atts.addFlashAttribute("name", name);
}

if (!StringUtils.isEmptyOrWhitespace(occupation)) {

    atts.addFlashAttribute("occupation", occupation);
}

We store the filled input parameters as flash attributes with addFlashAttribute if they are not empty and do not contain only blank spaces.

atts.addFlashAttribute("messages", errorMessages);

The error messages are stored as a flash attribute.

return new RedirectView("/");

We redirect to the form page with RedirectView.

templates/index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.css"
            rel="stylesheet">
</head>
<body>

<section class="ui container">

    <ul th:each="message : ${messages}">
        <li th:text="${message}" class="ui error message" />
    </ul>

    <form class="ui form" action="message" method="post">

        <div class="field">
            <label>Name:</label>
            <input type="text" name="name" th:value="${name}">
        </div>

        <div class="field">
            <label>Occupation:</label>
            <input type="text" name="occupation" th:value="${occupation}">
        </div>

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

    </form>
</section>

<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.js"></script>
</body>
</html>

This is the home page template. It sends a form with two inputs: name and occupation. The styling is done with Semantic UI library.

<ul th:each="message : ${messages}">
    <li th:text="${message}" class="ui error message" />
</ul>

If there are any error messages, they are displayed.

templates/showMessage.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Message</title>
</head>
<body>

<p th:text="${message}"/>

</body>
</html>

The showMessage template shows a message when the form was successfully processed.

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application  {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application is the entry point which sets up Spring Boot application.

In this article we have worked with flash attributes in Spring Boot.

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 Boot tutorials.