Prof. Me. Danilo Bueno
O que foi feito nas aulas anteriores?
INSERT
A aplicação tem dados de verdade, mas um endereço só.
Uma única rota devolve a lista inteira:
@GetMapping("/produtos")public String listar(Model model) { model.addAttribute("produtos", produtoRepository.findAll()); return "produtos";}
Como abrir um produto específico?
Como levar o usuário da lista até um item?
O que é um recurso?
Na Web, aquilo que tem endereço próprio é chamado de recurso.
/produtos?id=7 /produtos/7
Analisando as duas formas:
7
/produtos?id=7
@RequestParam
/produtos/7
@PathVariable
Por que existem duas maneiras de dizer a mesma coisa?
/produto.php?id=7 o caminho nomeia um programa /produtos/7 o caminho nomeia uma coisa
Nenhuma especificação da Web define que /produtos/7 é o produto 7: isso é convenção.
?ordem=preco
A regra prática: o que identifica vai no caminho, o que ajusta a consulta vai na query string.
Uma rota com um trecho variável
@GetMapping("/produtos/{id}")
/produtos/42
/produtos/103
@GetMapping("/produtos/{id}")public String detalhar(@PathVariable Long id, Model model) { Produto produto = produtoRepository.buscarPorId(id); model.addAttribute("produto", produto); return "produto";}
Método detalhar
detalhar
{id}
@PathVariable Long id
Long
buscarPorId(id)
addAttribute("produto", ...)
return "produto"
produto.ftlh
O endereço /produtos/abc chega à mesma rota:
/produtos/abc
abc
O tipo declarado no @PathVariable funciona como primeira validação do endereço.
A consulta agora precisa de filtro
SELECT * FROM produtos WHERE id = ?
?
buscarPorId
Consultando sem exigir que o registro exista
public Produto buscarPorId(Long id) { String sql = "SELECT * FROM produtos WHERE id = ?"; List<Map<String, Object>> linhas = jdbcTemplate.queryForList(sql, id); if (linhas.isEmpty()) { return null; } return Produto.deMap(linhas.get(0));}
Método buscarPorId
queryForList
linhas.isEmpty()
return null
linhas.get(0)
id
deMap
O null chega ao controller
null
O código correto para "esse recurso não existe" é 404 Not Found.
@GetMapping("/produtos/{id}")public String detalhar(@PathVariable Long id, Model model) { Produto produto = produtoRepository.buscarPorId(id); if (produto == null) { throw new ResponseStatusException(HttpStatus.NOT_FOUND); } model.addAttribute("produto", produto); return "produto";}
Uso do ResponseStatusException
ResponseStatusException
HttpStatus.NOT_FOUND
A verificação vem antes de qualquer uso do objeto, pelo mesmo motivo da validação no cadastro.
O template produto.ftlh
<h1>${produto.nome}</h1><p>Preço: R$ ${produto.preco?c}</p><a href="/produtos">Voltar para a lista</a>
O template recebe um único objeto, por isso não há <#list>.
<#list>
Cada item da lista vira um link
<#list produtos as produto> <li> <a href="/produtos/${produto.id?c}">${produto.nome}</a> </li></#list>
Construção do endereço
${produto.id?c}
Sem o ?c, o id 1998 sairia como 1,998, e o endereço não funcionaria.
?c
1,998
@Controllerpublic class ProdutoController { @Autowired private ProdutoRepository produtoRepository; @GetMapping("/produtos") public String listar(Model model) { ... } @GetMapping("/produtos/{id}") public String detalhar(@PathVariable Long id, Model model) { ... }}
Duas rotas que começam igual, distinguidas pelo trecho final do caminho.
[ Navegador ] GET /produtos/7 ▼ [ Controller ] @PathVariable, id = 7 ▼ [ Repositório ] SELECT ... WHERE id = ? ▼ [ Banco ] devolve zero ou uma linha ▼ [ Controller ] 404 se vazio, senão envia ao template ▼ [ View ] produto.ftlh exibe os detalhes
Prof. Me. Danilo Bueno [email protected]