Atividade Navegação entre Recursos (EstanteWeb)
Disciplina: Programação para Web 3
Assunto: URLs parametrizadas, Path Variables e páginas de detalhes
Professor: Prof. Me. Danilo Bueno
Grupos: duplas
Duração: 4 aulas de 50 minutos (em sala)
Importante
Antes de começar, leia o material de apoio sobre navegação entre recursos. É imprescindível ler antes de codar: a atividade aplica exatamente os conceitos do material. Se tiver dúvidas, releia, pergunte ao professor ou aos colegas. Você também pode consultar os slides da aula.
Contextualização
O site da EstanteWeb já lista o acervo e permite cadastrar títulos novos, mas a listagem é um beco sem saída: o cliente vê os livros em uma única tela corrida e não consegue abrir nenhum deles. Não há como ver a ficha de um título, nem como mandar o link de um livro para um amigo, porque o livro não tem endereço. Nesta etapa, a dupla vai dar a cada livro do acervo uma página própria, tratar corretamente os endereços que não levam a lugar nenhum e ligar a listagem às fichas.
O que já temos
O checkpoint de partida (semana-09-inicio) traz a aplicação Spring Boot das semanas anteriores, já rodando em http://localhost:8080, com:
LivroRepository(@Repository), com@Autowired JdbcTemplate, os métodos de consulta (listarTodos(),contarLivros(),listarAtePreco(double),somarValorAcervo()) e o métodosalvar(Livro), que executa oINSERT.Livro, com construtor sem argumentos, getters, setters e o método estáticodeMap(Map).LivroController(@Controller), com/livros/htmlexibindo a listagem pelo templatelivros.ftlh,GET /livros/novoexibindo o formulário ePOST /livrosgravando com validação.SugestaoRepositoryeSugestaoController, já gravando sugestões no banco.- Templates
cabecalho.ftlh,livros.ftlh,livro-novo.ftlh,contato.ftlh,contato-sucesso.ftlhesugestao-nova.ftlh. - Banco MySQL com as tabelas
livroesugestaopopuladas.
Como começar
git clonedo repositório do projeto (pule este passo se sua dupla já clonou nas semanas anteriores).git checkout semana-09-iniciopara partir exatamente do estado descrito acima.- Confirme que o container MySQL está no ar (
docker ps). Se não estiver, suba-o comdocker start estanteweb-mysql. - Rode a aplicação e confirme que
http://localhost:8080/livros/htmlexibe o acervo vindo do banco. - No terminal do MySQL, rode
SELECT id, titulo FROM livro;e anote dois identificadores que existem e um que não existe. Você vai usá-los nos testes.
Como funciona
- A atividade é feita em dupla, mas as entregas no Moodle são individuais.
- As missões são incrementais: cada uma constrói sobre a anterior, siga na ordem.
- Ao final de cada missão, teste no navegador antes de seguir.
- Responda, no caminho, às perguntas em destaque (marcadas com
>). Elas voltam no questionário do Moodle.
Cronograma
| Aula | Foco | Missões |
|---|---|---|
| 1 | A ficha de um livro | Missão 1 |
| 2 | Endereços que não levam a lugar nenhum | Missão 2 |
| 3 | Navegação de ida e volta | Missão 3 |
| 4 | Caminho ou query string + desafio | Missão 4 |
Missão 1: A ficha de um livro
Objetivo: fazer com que cada livro do acervo tenha um endereço próprio e uma página que o exiba.
1.1. Em LivroRepository, crie o método buscarPorId(Long id), que executa SELECT * FROM livro WHERE id = ? com queryForList, devolve null quando a consulta não trouxer nenhuma linha e, caso contrário, converte a primeira linha com Livro.deMap(...).
1.2. Crie o template livro.ftlh, com <#include "cabecalho.ftlh"> no topo, exibindo título, autor, ano e preço do livro recebido. Lembre-se de que esta página recebe um objeto, não uma lista.
1.3. Em LivroController, crie a rota GET /livros/{id}, recebendo o identificador com @PathVariable Long id, buscando o livro no repositório, enviando-o ao Model e retornando "livro".
1.4. Acesse no navegador o endereço de um dos identificadores que você anotou (por exemplo, http://localhost:8080/livros/1) e confirme que a ficha aparece com os dados corretos.
Por que o identificador do livro foi colocado no caminho da URL, e não na query string? Cite uma vantagem concreta dessa escolha para o cliente da loja.
Missão 2: Endereços que não levam a lugar nenhum
Objetivo: fazer a aplicação responder corretamente quando o endereço é válido na forma, mas não corresponde a nenhum livro.
2.1. Acesse o endereço do identificador que não existe (por exemplo, http://localhost:8080/livros/999) e observe o que acontece. Anote a mensagem de erro e o código de status exibido na aba Network do DevTools.
2.2. No GET /livros/{id}, verifique se o livro devolvido pelo repositório é null e, nesse caso, lance new ResponseStatusException(HttpStatus.NOT_FOUND) antes de usar o objeto. Teste novamente o endereço inexistente e confirme, no DevTools, que o status agora é 404.
2.3. Acesse http://localhost:8080/livros/abc e observe o status devolvido no DevTools. Anote qual é e em que momento ele acontece: antes ou depois de o corpo do método executar.
Os endereços
/livros/999e/livros/abcdevolvem códigos de status diferentes. Explique o que cada código informa ao navegador e por que não faria sentido usar o mesmo nos dois casos.
Missão 3: Navegação de ida e volta
Objetivo: ligar a listagem às fichas, de modo que o cliente navegue pelo acervo sem digitar endereços.
3.1. Em livros.ftlh, transforme o título de cada livro em um link para a ficha correspondente, montando o endereço a partir do identificador do próprio registro.
3.2. Clique em um dos links e confirme que a ficha correta abre. Se o endereço aparecer com vírgula (algo como /livros/1,998), ajuste a impressão do número no template.
3.3. Em livro.ftlh, acrescente um link “Voltar para o acervo”, apontando para a listagem.
3.4. Percorra o fluxo completo: abra a listagem, entre em uma ficha, volte, entre em outra. Use também o botão voltar do navegador e confirme que ele se comporta como esperado.
O que acontece com o endereço gerado no link quando o identificador do livro é impresso sem o tratamento de formatação de número? Explique por que o problema só aparece a partir de certos valores.
Missão 4: Caminho ou query string + desafio
Objetivo: usar as duas formas de passar valor pela URL na mesma tela e perceber a diferença entre elas na prática.
4.1. Em LivroController, altere a rota GET /livros/html para aceitar um parâmetro opcional de ordenação pela query string, recebido com @RequestParam(required = false) String ordem.
4.2. Em LivroRepository, crie o método listarOrdenadoPor(String coluna), que monta o SELECT * FROM livro ORDER BY ... apenas quando a coluna recebida for uma das permitidas (titulo, autor, ano ou preco), e cai na listagem padrão em qualquer outro caso. Use a listagem sem ordenação quando o parâmetro não vier.
4.3. Teste http://localhost:8080/livros/html?ordem=preco e http://localhost:8080/livros/html?ordem=titulo. Confirme que a listagem muda de ordem e que os links das fichas continuam funcionando. Teste também sem o parâmetro.
4.4. Desafio (opcional): na ficha de um livro, exiba também os outros títulos do mesmo autor. Crie em LivroRepository o método listarDoMesmoAutor(String autor, Long idAtual), usando SELECT * FROM livro WHERE autor = ? AND id <> ?, e liste os resultados em livro.ftlh como links para as respectivas fichas. Se não houver outros títulos, a seção não deve aparecer.
Na Missão 4 o valor da ordenação foi para a query string, enquanto na Missão 1 o identificador do livro foi para o caminho. Justifique por que cada valor foi parar onde foi, usando a diferença entre identificar um recurso e ajustar a forma de exibi-lo.
Entrega individual
Cada integrante da dupla deve, individualmente, no Moodle:
- Responder o questionário da semana (perguntas de reflexão sobre a atividade).
- Enviar o projeto compactado em
.zip(a pasta do projeto sem a subpastatarget/, que é gerada na compilação e pesa à toa).