Programação para Web 3

Formulários e Fluxo de Navegação

Prof. Me. Danilo Bueno

Agenda

  1. O problema da confirmação solta
  2. Formulário grudento
  3. Variáveis ausentes: ?? e !
  4. Post/Redirect/Get
  5. Construindo a reserva, passo a passo

O problema

Confirmação sem contexto

@PostMapping("/reserva")public ResponseEntity<String> receberReserva(...) {    return ResponseEntity.ok("Reserva registrada.");}
  • A resposta não leva o usuário a nenhuma página seguinte
  • Um erro de validação descarta os dados já digitados no formulário

Duas consequências práticas

  • Recarregar essa resposta reenvia a mesma requisição POST
  • Não existe uma URL própria para essa confirmação: não é possível voltar a ela nem compartilhá-la

Os próximos tópicos resolvem cada um desses dois pontos.

Os dois caminhos de um envio

Todo POST de formulário termina numa decisão do servidor:

  • Dados inválidos: devolver o formulário, com o erro e o que já foi digitado
  • Dados válidos: levar o usuário adiante, para outra página

Cada metade desta aula trata de um desses caminhos.

Formulário grudento

Validação no cliente e no servidor

  • O navegador valida por conta própria, com atributos como required e type="email"
  • Essa verificação pode ser removida pelo DevTools ou ignorada por outra ferramenta

A validação no cliente melhora a experiência de uso; apenas a validação no servidor protege a aplicação.

O formulário que lembra

  • Ao detectar um erro de validação, o controller reprocessa o mesmo template
  • O Model recebe o motivo do erro e os valores já enviados pelo usuário

Sem essa reexibição, o conteúdo digitado se perde a cada tentativa mal-sucedida.

Devolvendo o formulário com erro

if (telefone.isBlank()) {    model.addAttribute("erro", "Informe um telefone.");    model.addAttribute("nome", nome);    return "reserva";}

O retorno continua sendo o nome do template, não mais um corpo de erro cru.

E quando não há erro?

  • Na primeira visita ao formulário, nenhum erro foi adicionado ao Model
  • O template, porém, referencia ${erro} de qualquer maneira

Referenciar uma variável ausente interrompe a geração da página, como visto na aula de templates.

Variáveis ausentes: ?? e !

O operador ??

<#if erro??>    <p>${erro}</p></#if>

Verifica se a variável existe no modelo de dados, sem lançar erro.

O operador !

<input name="nome" value="${nome!}">

Se nome não existir no modelo, imprime string vazia em vez de quebrar a página.

Post/Redirect/Get

O aviso do navegador

  • Recarregar a resposta de um POST reenvia a mesma requisição
  • Risco real: duplicar uma compra, duplicar um cadastro

Este é o aviso emitido pelo navegador ao recarregar uma página originada de um POST.

O padrão Post/Redirect/Get

POST /reserva            ──▶ 302 Found
                              Location: /reserva/confirmada

GET  /reserva/confirmada ──▶ 200 OK

É um padrão consolidado da Web, cujo nome descreve a própria sequência de requisições.

HTTP não guarda estado

  • Cada requisição é independente das anteriores, como visto na primeira aula
  • O Model preenchido durante o POST não existe mais quando o GET seguinte chega

Qualquer informação que precise atravessar o redirecionamento tem de viajar de forma explícita, na própria URL.

O prefixo redirect:

return "redirect:/reserva/confirmada";

Deixa de ser nome de view: vira instrução de redirecionamento HTTP (302).

Por que 302?

  • 302 Found: redirecionamento temporário, o padrão do redirect: no Spring
  • 301 Moved Permanently: indicaria que o caminho antigo nunca mais deve ser usado

A escolha do código comunica a intenção do redirecionamento, não só o fato de que ele ocorreu.

Construindo a reserva, passo a passo

O formulário de reserva

<!-- reserva.ftlh --><#include "cabecalho.ftlh"><form method="post" action="/reserva">    <input type="text" name="nome" value="${nome!}">    <input type="text" name="telefone">    <button type="submit">Reservar</button></form>

Servindo o formulário

@GetMapping("/reserva")public String reserva() {    return "reserva";}

O template ainda não recebe nenhuma variável do controller.

Validando no POST

@PostMapping("/reserva")public String receberReserva(Model model,        @RequestParam String nome, @RequestParam String telefone) {    if (telefone.isBlank()) {        model.addAttribute("erro", "Informe um telefone.");        model.addAttribute("nome", nome);        return "reserva";    }    // sucesso: próximo passo}

Exibindo o erro no template

<!-- reserva.ftlh --><#include "cabecalho.ftlh"><#if erro??>    <p class="erro">${erro}</p></#if><form method="post" action="/reserva">    <input type="text" name="nome" value="${nome!}">    <input type="text" name="telefone">    <button type="submit">Reservar</button></form>

A página de confirmação

<!-- reserva-confirmada.ftlh --><#include "cabecalho.ftlh"><p>Reserva confirmada!</p>

Servindo a confirmação

@GetMapping("/reserva/confirmada")public String confirmada() {    return "reserva-confirmada";}

Redirecionando no sucesso

@PostMapping("/reserva")public String receberReserva(Model model,        @RequestParam String nome, @RequestParam String telefone) {    if (telefone.isBlank()) {        model.addAttribute("erro", "Informe um telefone.");        model.addAttribute("nome", nome);        return "reserva";    }    return "redirect:/reserva/confirmada";}

O mesmo método agora cobre os dois caminhos: erro devolve o formulário, sucesso redireciona.

Referência rápida

Conceito Papel
Validação no servidor A única que protege de fato; a do cliente é conveniência
Formulário grudento Re-renderiza o formulário com erro e valores digitados
?? Verifica se uma variável existe no modelo
! Valor padrão quando a variável não existe
HTTP sem estado Cada requisição é independente; nada sobrevive entre elas
Post/Redirect/Get POST responde com redirect, evitando reenvio no F5
redirect:/caminho Transforma o retorno em instrução de redirecionamento
302 Found Status padrão do redirect:: redirecionamento temporário

Obrigado!

Prof. Me. Danilo Bueno
[email protected]