Browser ──── HTTP GET /produtos ────► Servidor │ route handler │ consulta BD │ renderiza HTML Browser ◄──── HTTP 200 + HTML ──────── Servidor
Servidor: recebe request → processa → devolve resposta (HTML, JSON, ficheiro).
O backend nunca confia no frontend — validação sempre no servidor.
from flask import Flask app = Flask(__name__) @app.route("/") def home(): return "<h1>Olá, Aulify!</h1>" @app.route("/sobre") def sobre(): return "Página sobre" if __name__ == "__main__": app.run(debug=True)
flask run → http://localhost:5000.
flask run
http://localhost:5000
@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}" # /pesquisa?q=python @app.route("/criar", methods=["GET", "POST"]) def criar(): if request.method == "POST": nome = request.form["nome"] # guardar... return render_template("form.html")
@app.route("/produtos") def produtos(): lista = [{"nome": "Caneta", "preco": 1.5}] return render_template("produtos.html", produtos=lista)
<!-- templates/produtos.html --> <ul> {% for p in produtos %} <li>{{ p.nome }} — {{ p.preco }}€</li> {% endfor %} </ul>
{{ }} escapa automaticamente (anti-XSS). {% %} para lógica.
{{ }}
{% %}
<!-- base.html --> <!DOCTYPE html> <html><head><title>{% block titulo %}{% endblock %}</title></head> <body> {% block conteudo %}{% endblock %} </body></html>
<!-- pagina.html --> {% extends "base.html" %} {% block titulo %}Produtos{% endblock %} {% block conteudo %} <h1>Lista</h1> {% endblock %}
DRY: layout comum num só sítio.
GET /api/produtos
POST /api/produtos
PUT /api/produtos/5
PATCH /api/produtos/5
DELETE /api/produtos/5
Recursos no plural, sem verbos na URL (/produtos não /getProdutos).
/produtos
/getProdutos
from flask import jsonify, request @app.route("/api/produtos") def listar(): return jsonify(produtos) @app.route("/api/produtos/<int:id>") def get(id): p = next((x for x in produtos if x["id"] == id), None) if p is None: return jsonify({"erro": "não encontrado"}), 404 return jsonify(p) @app.route("/api/produtos", methods=["POST"]) def criar(): dados = request.get_json() # validar... return jsonify(novo), 201
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Produto(BaseModel): nome: str preco: float @app.get("/produtos") def listar() -> list[Produto]: return produtos @app.post("/produtos", status_code=201) def criar(p: Produto): produtos.append(p) return p
Validação automática + docs OpenAPI em /docs.
/docs
from flask_sqlalchemy import SQLAlchemy app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db" db = SQLAlchemy(app) class Produto(db.Model): id = db.Column(db.Integer, primary_key=True) nome = db.Column(db.String(150), nullable=False) preco = db.Column(db.Float) @app.route("/produtos") def listar(): produtos = Produto.query.all() return render_template("lista.html", produtos=produtos)
from werkzeug.security import generate_password_hash, check_password_hash from flask_login import login_user, login_required, current_user # Registo hash = generate_password_hash(senha) # nunca texto plano # Login if check_password_hash(user.pwd_hash, senha): login_user(user) # Proteger rota @app.route("/painel") @login_required def painel(): return f"Olá {current_user.nome}"
app/ ├── app.py # entry point ├── models.py # modelos BD ├── routes/ # blueprints ├── templates/ # Jinja2 ├── static/ # CSS, JS, imagens ├── requirements.txt ├── .env # secrets (gitignore) └── .gitignore
gunicorn
DEBUG=False
gunicorn app:app --workers 4 --bind 0.0.0.0:8000