Validation

Validation is the process of checking if the data is valid before saving it to the database (or similar). The object used to store the data is annotated with validation annotations.

Model

In the example below, the title field must be between 2 and 30 characters long and the description field must be between 10 and 50 characters long.

AnnotationDescriptionExample
@DigitsElement must be a number from the accepted range.@Digits(integer = 2, fraction = 0)
@EmailString has to be a well-formatted email address.@Email
@Max / @MinThe number must be (lower or equal)/(higher or equal) to the specified value.@Max(value = 42)
@Negative / @NegativeOrZeroNumber must be negative (or zero).@Negative
@Positive / @PositiveOrZeroNumber must be positive (or zero).@Positive
@NotBlankElement must not be null and contain at least one non-whitespace character.@NotBlank
@NotEmptyElement must not be null nor empty@NotEmpty
@NotNull / @NullElement must (not) be null.@NotNull
@SizeThe elements size must be between defined boundaries (inclusive)@Size(min = 5, max = 10)

A list of all validation attributes can be found in the documentation.

  @Document(collection = "questionnaires")
public class Questionnaire {
    @Id
    private String id;
    @Size(min = 2, max = 30)
    private String title;
    @Size(min = 10, max = 50)
    private String description;

    public Questionnaire() {}

    public Questionnaire(String title, String description) {
        this.title = title;
        this.description = description;
    }
}
  

Controller

The validation can then be executed using the @Valid annotation in the controller method. The result of the validation is stored in the BindingResult object.

  @PostMapping
public String create(@Valid Questionnaire questionnaire, BindingResult result) {
    if (result.hasErrors()) {
        return "questionnaires/create";
    }
    questionnaireRepository.save(questionnaire);
    return "redirect:/questionnaires";
}

@PutMapping(value = "/{id}")
public String put(@PathVariable String id, @Valid Questionnaire questionnaire, BindingResult result) {
    if (result.hasErrors()) {
        return "questionnaires/update";
    }
    if (!questionnaireRepository.existsById(id)) {
        return "404";
    }
    questionnaireRepository.save(questionnaire);
    return "redirect:/questionnaires";
}
  

View

Error messages can be displayed using the th:errors attribute. The th:if="${#fields.hasErrors('...')}" is required so that the error message is only displayed if there are errors.

  <!DOCTYPE html>
<html th:replace="~{layout :: maincontent(~{::section})}">
<head>
    <title>flashcard</title>
</head>
<body>
<section style="width: 60%; margin-left: 10em">
    <form th:action="@{/questionnaires}" th:object="${questionnaire}" method="post">
        <fieldset>
            <div class="mb-3 row">
                <label class="col-md-2" th:for="title">Title:</label>
                <div class="col-md-10">
                    <input class="form-control" type="text" th:field="*{title}" autofocus="autofocus"/>
                    <span th:if="${#fields.hasErrors('title')}" th:errors="*{title}" class="text-danger"/>
                </div>
            </div>
            <div class="mb-3 row">
                <label class="col-md-2" th:for="description">Description:</label>
                <div class="col-md-10">
                    <input class="form-control" type="text" th:field="*{description}"/>
                    <span th:if="${#fields.hasErrors('description')}" th:errors="*{description}" class="text-danger"/>
                </div>
            </div>
        </fieldset>
        <div class="mb-3 row row-cols-auto">
            <a class="btn btn-light" th:href="@{/questionnaires}">
                <i class="bi bi-x-circle-fill"></i> Cancel
            </a>
            <button type="submit" class="btn btn-primary">
                <i class="bi bi-floppy-fill"></i> Save
            </button>
        </div>
    </form>
</section>
</body>
</html>