Spring Framework <form:errors/> tag not showing errors

lalsberg picture lalsberg · Apr 26, 2014 · Viewed 20.5k times · Source

I know there are many similar questions here, but none of them solved my problem.

I'm using Spring 4.0.3 and Hibernate Validator 5.1.0. The problem occurs when I try to omit the path attribute of the <form:errors/> tag, so:

<form:errors path="contato.nome" /> works
<form:errors path="*" /> works
<form:errors /> doesn't work

I don't know why it happens. Spring javadocs (org.springframework.web.servlet.tags.form.ErrorsTag) says it should work like that:

Field only - set path to the field name (or path)
Object errors only - omit path
All errors - set path to *

Can you help me, please?

The interested code is in the 'edicao.jsp' and in the method 'confirmarEdicao' of the ContatoController.java. Sorry if my english is bad.


ContatoController.java

@Controller
@RequestMapping("/contatos")
public class ContatoController {

    @Autowired
    private ContatoService contatoService;

    @Autowired 
    private MessageSource messageSource;

    @RequestMapping(value = "/confirmarEdicao", method = RequestMethod.POST)
    public String confirmarEdicao(@Valid Contato contato, BindingResult bindingResult) {

        if(bindingResult.hasErrors()) {
            return "contatos/edicao";
        }

        contatoService.save(contato);
        return "redirect:/contatos";
    }

    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView form(HttpServletRequest request) {
        String message = messageSource.getMessage("teste", null, new Locale("pt", "BR"));
        System.out.println(message);
        return new ModelAndView("contatos/listagem")
            .addObject("contatos", contatoService.list());
    }

    @RequestMapping("/remover/{id}")
    public String remover(Contato contato) {
        contatoService.delete(contato);
        return "redirect:/contatos";
    }

    @RequestMapping("/editar/{id}")
    public ModelAndView formEdicao(Contato contato) {
        contato = contatoService.find(contato.getId());
        return new ModelAndView("contatos/edicao")
            .addObject(contato);
    }


    @RequestMapping(value = "/cadastrar")
    public String formCadastro() {
        return "contatos/cadastro";
    }

    @RequestMapping(value = "/confirmarCadastro", method = RequestMethod.POST)
    public String confirmarCadastro(@Valid Contato contato, BindingResult bindingResult, 
            RedirectAttributes redirectAttributes) {        

        if (bindingResult.hasFieldErrors()) {
            return "contatos/cadastro";
        }

        contatoService.save(contato);
        redirectAttributes.addFlashAttribute("mensagem", "Contato cadastrado com sucesso.");
        return "redirect:/contatos";
    }   

    @ResponseBody
    @RequestMapping(value = "/pesquisar/{nome}", method = RequestMethod.GET, 
        produces="application/json")
    public List<Contato> pesquisar(@PathVariable String nome) {
        return contatoService.findByName(nome);
    }

}

edicao.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Editar contato</title>
</head>
<body>

<c:set var="context">${pageContext.request.contextPath}</c:set>
<script type="text/javascript">var context = "${context}";</script>
<script src="${context}/resources/js/jquery-2.1.0.min.js"></script>
<script src="${context}/resources/js/contatos/edicao.js"></script>

<form:form commandName="contato" action="${context}/contatos/confirmarEdicao" method="post">
<form:errors/>
    <table>
        <form:hidden path="id"/>
        <tr>
            <td>Nome:</td> 
            <td><form:input path="nome" /></td>
        </tr>
        <tr>
            <td>Telefone:</td>
            <td><form:input path="telefone"/></td>
        </tr>
        <tr>
            <td><input type="button" value="Voltar" id="btn_voltar"/><input type="submit" value="Salvar"/></td>         
        </tr>
    </table>
</form:form>

</body>
</html>

Contato.java

package com.handson.model;

import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotEmpty;

public class Contato {

    private Long id;

    @Size(min = 3, message = "Nome deve ter no mínimo 3 caracteres")
    @NotEmpty(message = "O nome deve ser preenchido")
    private String nome;

    private String telefone;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getTelefone() {
        return telefone;
    }

    public void setTelefone(String telefone) {
        this.telefone = telefone;
    }

    public Contato withId(Long id) {
        setId(id);
        return this;
    }

    public Contato withTelefone(String telefone) {
        setTelefone(telefone);
        return this;
    }

    public Contato withNome(String nome) {
        setNome(nome);
        return this;
    }

    @Override
    public String toString() {
        return "Contato [id=" + id + ", nome=" + nome + ", telefone="
                + telefone + "]";
    }

}

Answer

Pavel Horal picture Pavel Horal · Apr 28, 2014

There are some keywords which should be defined:

  • path - EL-like path to an object or to a field of an object (e.g. foo, foo.bar or foo.bar.baz)
  • nested path - current path context stored as nestedPath request attribute (new paths are relative to this path)
  • object error - error connected with object itself (e.g. path is equal to foo)
  • field error - error connected with object field (e.g. path is foo.bar)

The tag <form:form commandName="foo"> defines nested path as nestedPath=foo. When you write <form:errors path="bar"> it tries to find errors defined for path foo.bar.

Lets say that you have errors connected with foo (object error), foo.bar and foo.bar.baz (nested field error). What this means:

  • if you enter <form:errors>, only errors bound to foo path are displayed => 1 message
  • if you enter <form:errors path="bar">, only errors bound to foo.bar path are displayed => 1 message
  • if you enter <form:errors path="*">, errors bound to foo and its child paths are displayed => 3 messages
  • if you enter <form:errors path="bar.*">, only child errors for foo.bar are diplayed => 1 message
  • if you enter <form:errors path="bar*">, errors bound to foo.bar and its child paths are diplayed => 2 messages

Checking on Errors class JavaDoc might give you additional insight.