Solucionado (ver solução)
Solucionado
(ver solução)
17
respostas

Erro de Execution exception

Boa tarde, Primeiramente meus parabens pelo curso! Aguardando os próximos.

Ao realizar o cadastro do produto com os campos vazios me deparei com esse erro:

[CompletionException: java.lang.IllegalStateException: Error(s) binding form: {"preco":["Campo preço é obrigatório"],"codigo":["Campo código é obrigatório"],"titulo":["Campo título é obrigatório"]}]

realizei o debug e ele para nessa linha

Form<Produto> formulario = formularios.form(Produto.class).bindFromRequest();

e depois

Produto produto = formulario.get();

e já retorna o erro em vermelho, nem chega a passar pelo if do validador de produtos.

meu código esta assim:

   public Result salvaNovoProduto() {
        Form<Produto> formulario = formularios.form(Produto.class).bindFromRequest();
        Produto produto = formulario.get();

        if (validatorDeProduto.temErros(formulario)) {
            flash("danger", "Existem erros no seu formulário!");
            return badRequest(formularioDeNovoProduto.render(formulario));
        }
        produto.save();
        flash("success", "Seu produto '" + produto.getTitulo() + "' foi cadastrado com sucesso!");
        return redirect(routes.ProdutoController.formularioDeNovoProduto());
    }
17 respostas

Acabo colocando depois do bindfromrequest a linha a seguir e funciona:

if (formulario.hasErrors()) {
            return badRequest(formularioDeNovoProduto.render(formulario));
}

existe uma forma de colocar esse erro no validador do produto? sem precisar desse if?

O que está acontecendo é que o seu formulário não é válido, correto? Então o Play! está reconhecendo isso e te protegendo. Como? Ele lança uma exceção quando você tenta pegar o produto do formulário com o formulario.get();.

Você não precisa utilizar o objeto produto antes de fazer a validação, então mude essa linha lá pra baixo, logo antes do produto.save().

Quanto ao ValidadorDeProduto, como está seu código? Pergunto isso pois se reparar, na aula a gente retorna esse formulario.hasErrors() no fim do validadorDeProduto.temErros(formulario). Isso confere tanto as validações por anotação quanto as customizadas que a gente faz dentro do validador, então não precisa ser utilizada do lado de fora (o .temErros(formulario) faz esse papel!).

Ps.: Obrigado! Foi interessante gravar o curso, é legal ter o trabalho reconhecido ^^

Marco

O salvar do produto controller esta assim:

    @Inject
    private ValidatorDeProduto validatorDeProduto;

    public Result salvaNovoProduto() {
        Form<Produto> formulario = formularios.form(Produto.class).bindFromRequest();

        if (validatorDeProduto.temErros(formulario)) {
            flash("danger", "Existem erros no seu formulário!");
            return badRequest(formularioDeNovoProduto.render(formulario));
        }

        Produto produto = formulario.get();
        produto.save();
        flash("success", "Seu produto '" + produto.getTitulo() + "' foi cadastrado com sucesso!");
        return redirect(routes.ProdutoController.formularioDeNovoProduto());
    }

e o validator esta assim:

    @Inject
    private ProdutoDAO produtoDAO;

    public boolean temErros(Form<Produto> formulario) {

        Produto produto = formulario.get();

        if (produto.getPreco() < 0.0) {
            formulario.reject(new ValidationError("preco", "O preço do produto deve ser maior que 0!"));
        }

        if (produtoDAO.comCodigo(produto.getCodigo()).isPresent()) {
            formulario.reject(new ValidationError("codigo", "Já existe um produto com este código!"));
        }

        return formulario.hasErrors();

    }

só um pequeno detalhe que o nome do meu validador acabei deixando validator* mas o resto é igual a aula :D

Peço desculpas pelo código sem formatação, estou com díficuldades em entender como funciona.

Ao escrever, tem um botão à esquerda em cima da caixa de texto chamado {} inserir código. Clique nele e coloque seu código entre os dois grupos de crases. ;)

Estive debugando o código novamente e agora entra no if de validação do formulário e passa pela linha

Produto produto = formulario.get();

e o erro fica por aí. Ou seja, persiste o erro.

Valeu =D

Parece tudo ok! Repare que a última linha do seu validator é return formulario.hasErrors();, como eu disse.

Durante a produção do projeto criamos validações via anotação para que o preço e o código do produto fossem obrigatórios. Contando com isso, você não deveria conseguir enviar o formulário vazio, porém:

Nas primeiras linhas do validador, a gente pega o produto com Produto produto = formulario.get();. Considerando que a validação por anotações adiciona o atributo required no campo HTML, isso não deveria dar erros, mas remover esse atributo direto no HTML do navegador (para poder enviar os campos vazios) vai fazer essa linha de código lançar uma exceção. Remover totalmente a validação por anotação não causa erro algum.

Como você está recebendo erros, imagino que tenha feito do primeiro modo (alterar o HTML no navegador), ou uma outra opção: criou os campos utilizando HTML puro ou somente o helper da própria Play!, sem incluir o Bootstrap. Isso vai causar erros pois nenhum deles faz a validação HTML com a qual contamos ao criar o validador!

Então adicione o atributo required aos seus campos com <input name="... required> ou aos helpers com @helper.inputText(form("nomeDoCampo"), 'required -> true) e seu problema deve ser resolvido.

A outra opção é utilizar o valor dos campos diretamente no validador, que garante que o usuário não vai ver a exceção lançada. Troque no validador:

// Produto produto = formulario.get();
// troque por
String codigo = formulario.field("codigo").value();
Double preco = Double.parseDouble(formulario.field("preco").value());
if (preco < 0.0) {}//... etc
if (produtoDAO.comCodigo(codigo).isPresent()) {}//... etc

Claro que aí temos o problema possível do Double.parseDouble lançar uma exceção, então pode ser interessante utilizar formulario.field("preco").valueOr(-1.0) ou envolver o código em um try/catch.

Marco Salles

fiz a linha de código a seguir e funciona as validações:

        try {
            Produto produto = formulario.get();

            if (produto.getPreco() < 0.0) {
                formulario.reject(new ValidationError("preco", "O preço do produto deve ser maior que 0!"));
            }

            if (produtoDAO.comCodigo(produto.getCodigo()).isPresent()) {
                formulario.reject(new ValidationError("codigo", "Já existe um produto com este código!"));
            }

            return formulario.hasErrors();
        } catch (Exception e) {
            return formulario.hasErrors();
        }

Apenas envolvi em um try catch retornando o formulário com erros. É uma boa solução?

solução!

Funciona, mas não é uma solução ideal. Isso porque você perde alguns dados. Por exemplo, se houver algum campo em branco, sendo que o preço está preenchido, porém com um valor abaixo de zero. Você perde a validação de que o preço está abaixo de zero pois o código não chegou ali.

A solução mais adequada é utilizar o dado dos campos como mostrei no último código. Ficaria assim:

String codigo = formulario.field("codigo").value();
double preco = -1.0;
try {
    preco = Double.parseDouble(formulario.field("preco").value());
} catch (Exception e) {
    formulario.reject(new ValidationError("preco", "O preço do produto deve ser um número!"));
}
if (preco < 0.0) {
    formulario.reject(new ValidationError("preco", "O preço do produto deve ser maior que 0!"));
}
if (produtoDAO.comCodigo(codigo).isPresent()) {
    formulario.reject(new ValidationError("codigo", "Já existe um produto com este código!"));
}
return formulario.hasErrors();

Marco

Funcionou perfeitamente!

Muito legal essa validação de formulários! Adoro isso!

Mas no meu console ainda persiste um pequeno problema que é o seguinte:

[warn] play - You are using status code '400' with flashing, which should only be used with a redirect status!

Para resolver esse pequeno problema não é melhor remover esse flash e adicionar um formRefect?

Código como esta:

        if (validatorDeProduto.temErros(formulario)) {
            flash("danger", "Existem erros no seu formulário!");
            return badRequest(formularioDeNovoProduto.render(formulario));
        }

Para fazer essa transição teríamos que utilizar caching, assunto não abordado no curso. Isso porque o redirect não armazenaria os dados do formulário e portanto não conseguiríamos mostrar as mensagens de erro. Inserindo o formulario em um cache, isso seria possível, mas achei que ficaria complexo pra um curso básico.

Na realidade esse flash não é necessário já que as outras mensagens aparecem nos campos. Pode remover =)

No entanto, repare que a mensagem é somente um warning! Não te impede de utilizar o sistema normalmente.

Se quiser saber mais sobre cache, abra uma nova dúvida =)

E não esqueça de marcar o tópico aqui como solucionado! ^^

Muito obrigado Marco Salles

Trabalho com play framework a 1 ano e estou desenvolvendo uma biblioteca digital com Backend(play framework) e Frontend(Angularjs). O que foi passado na aula me ajudou a atualizar os conceitos e melhorar ainda mais a lógica do meu backend e as mensagens em json.

O meu projeto em play já tem implementado internacionalização, login, envia emails e várias outras funcionalidades.

Ultimamente busco uma forma de criar autenticação na minha aplicação utilizando as redes sociais como facebook, twitter ou até mesmo o google.

Acho que isso também seria interessante para os próximos cursos do play framework.

Atenciosamente

Opa, imagina!

O segundo curso de Play!, saído do forno, aborda exatamente os temas de autenticação, segurança e envio de emails (esse último sem nenhuma ferramenta de template)! O link vai abaixo se tiver interesse. =)

Play! Framework II

Muito bom!

Valeu!