Partilhar: WhatsApp
aulify
UC UC02834 · T. Desenv. Software

Ficha 01 · Rotas, templates e formulários

Flask básico, Jinja2, processar formulários
Versão · Aluno
Tempo · 60 minutos
Cotação · 100 pontos
Aluno(a)
Turma
Data
Objectivos da ficha

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>&copy; 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 &lt;, 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.