Ficha 01 · Rotas, templates e formulários
- Definir rotas
- Usar templates
- Processar formulários
- Aplicar herança de templates
Parte I · Rotas
Exercício 1 · Definir rotas (15 pts)
Escreve rotas Flask para:
a) / que devolve "Bem-vindo".
b) /produto/<id> que mostra o id (inteiro).
c) /pesquisa?q=... que mostra o termo pesquisado.
from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def home():
return "Bem-vindo"
@app.route("/produto/<int:id>")
def produto(id):
return f"Produto {id}"
@app.route("/pesquisa")
def pesquisa():
termo = request.args.get("q", "")
return f"A pesquisar: {termo}"
Exercício 2 · Métodos HTTP (10 pts)
a) Como permitir que uma rota aceite GET e POST?
b) Como distinguir, dentro da função, se o pedido é GET ou POST?
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
if request.method == "POST":
# processar dados submetidos
nome = request.form["nome"]
return f"Recebido: {nome}"
# GET — mostrar formulário
return render_template("contacto.html")
methods=["GET", "POST"] permite ambos; request.method distingue.
Parte II · Templates
Exercício 3 · Renderizar lista (20 pts)
Escreve a rota e o template Jinja2 para mostrar uma lista de alunos (nome + nota), com mensagem se a lista estiver vazia.
@app.route("/alunos")
def alunos():
lista = [
{"nome": "Ana", "nota": 17},
{"nome": "João", "nota": 14},
]
return render_template("alunos.html", alunos=lista)
<!-- templates/alunos.html -->
<h1>Alunos</h1>
<ul>
{% for a in alunos %}
<li>{{ a.nome }} — {{ a.nota }} valores</li>
{% else %}
<li>Sem alunos registados.</li>
{% endfor %}
</ul>
O {% else %} dentro do for executa se a lista estiver vazia.
Exercício 4 · Herança de templates (20 pts)
Cria um base.html com blocos titulo e conteudo, e uma página produtos.html que o estende.
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<title>{% block titulo %}Aulify{% endblock %}</title>
</head>
<body>
<header><h1>Aulify</h1></header>
<main>
{% block conteudo %}{% endblock %}
</main>
<footer>© 2026</footer>
</body>
</html>
<!-- templates/produtos.html -->
{% extends "base.html" %}
{% block titulo %}Produtos — Aulify{% endblock %}
{% block conteudo %}
<h2>Os nossos produtos</h2>
<ul>
{% for p in produtos %}
<li>{{ p.nome }}</li>
{% endfor %}
</ul>
{% endblock %}
Vantagem: o layout (header/footer) está num só sítio. Mudar uma vez muda todas as páginas.
Parte III · Formulários
Exercício 5 · Processar formulário (20 pts)
Cria uma rota /registar que:
- GET: mostra formulário (nome, email).
- POST: valida (nome ≥ 2 caracteres, email tem @), mostra erros ou regista.
@app.route("/registar", methods=["GET", "POST"])
def registar():
if request.method == "POST":
nome = request.form.get("nome", "").strip()
email = request.form.get("email", "").strip()
erros = []
if len(nome) < 2:
erros.append("Nome tem de ter pelo menos 2 caracteres")
if "@" not in email or "." not in email:
erros.append("Email inválido")
if erros:
return render_template("registar.html", erros=erros,
nome=nome, email=email)
# guardar utilizador (BD)...
return redirect(url_for("sucesso"))
return render_template("registar.html", erros=[])
<!-- templates/registar.html -->
{% extends "base.html" %}
{% block conteudo %}
{% if erros %}
<ul class="erros">
{% for e in erros %}<li>{{ e }}</li>{% endfor %}
</ul>
{% endif %}
<form method="post">
<label for="nome">Nome</label>
<input type="text" id="nome" name="nome" value="{{ nome or '' }}" required>
<label for="email">Email</label>
<input type="email" id="email" name="email" value="{{ email or '' }}" required>
<button type="submit">Registar</button>
</form>
{% endblock %}
Boas práticas: remostrar valores preenchidos em caso de erro; validar no servidor mesmo havendo required no HTML.
Exercício 6 · XSS (15 pts)
Um colega escreveu:
<p>{{ comentario|safe }}</p>
a) Qual o risco?
b) Como corrigir?
c) Quando é |safe legítimo?
a) |safe desliga o auto-escape. Se comentario vier de input do utilizador, alguém pode submeter <script>...</script> e executar JS no browser de outros (XSS).
b) Remover |safe:
<p>{{ comentario }}</p>
Jinja2 escapa automaticamente: < vira <, neutralizando o script.
Se precisar de formatação limitada, sanitizar no servidor com bleach e só então mostrar.
c) |safe é legítimo apenas com HTML de confiança gerado pelo próprio sistema (ex.: HTML produzido por um conversor Markdown controlado), nunca com input directo do utilizador.