En aquest tema, aprendrem a construir APIs REST utilitzant Flask, un microframework de Python que és lleuger i fàcil d'utilitzar. Les APIs REST són fonamentals per a la comunicació entre aplicacions web i serveis, i Flask proporciona les eines necessàries per crear-les de manera eficient.
Continguts
Introducció a REST
REST (Representational State Transfer) és un estil d'arquitectura per a la creació de serveis web. Els principis clau de REST inclouen:
- Client-Servidor: Separació de les responsabilitats entre el client i el servidor.
- Sense Estat: Cada petició del client al servidor ha de contenir tota la informació necessària per entendre i processar la petició.
- Cacheable: Les respostes del servidor poden ser emmagatzemades en cache per millorar el rendiment.
- Interfície Uniforme: Utilització de mètodes HTTP estàndard (GET, POST, PUT, DELETE) per a la comunicació.
Configuració de Flask
Instal·lació de Flask
Per començar, necessitem instal·lar Flask. Podem fer-ho utilitzant pip:
Creació d'un Projecte Flask
Creem un fitxer anomenat app.py i afegim el següent codi per configurar una aplicació bàsica de Flask:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hola, món!"
if __name__ == '__main__':
app.run(debug=True)Executem l'aplicació:
Ara, si obrim un navegador i anem a http://127.0.0.1:5000/, hauríem de veure el missatge "Hola, món!".
Creació de Rutes i Endpoints
En una API REST, les rutes defineixen els endpoints que el client pot utilitzar per interactuar amb el servidor. A continuació, afegim algunes rutes a la nostra aplicació Flask.
Exemple de Rutes
from flask import Flask, jsonify, request
app = Flask(__name__)
# Dades simulades
usuaris = [
{'id': 1, 'nom': 'Joan', 'email': 'joan@example.com'},
{'id': 2, 'nom': 'Maria', 'email': 'maria@example.com'}
]
@app.route('/usuaris', methods=['GET'])
def obtenir_usuaris():
return jsonify(usuaris)
@app.route('/usuaris/<int:id>', methods=['GET'])
def obtenir_usuari(id):
usuari = next((u for u in usuaris if u['id'] == id), None)
return jsonify(usuari) if usuari else ('', 404)
@app.route('/usuaris', methods=['POST'])
def crear_usuari():
nou_usuari = request.get_json()
usuaris.append(nou_usuari)
return jsonify(nou_usuari), 201
if __name__ == '__main__':
app.run(debug=True)Explicació del Codi
@app.route('/usuaris', methods=['GET']): Defineix una ruta per obtenir tots els usuaris.@app.route('/usuaris/<int:id>', methods=['GET']): Defineix una ruta per obtenir un usuari específic per ID.@app.route('/usuaris', methods=['POST']): Defineix una ruta per crear un nou usuari.
Gestió de Peticions i Respostes
Flask proporciona diverses funcions per gestionar peticions i respostes.
Exemple de Petició POST
@app.route('/usuaris', methods=['POST'])
def crear_usuari():
nou_usuari = request.get_json()
usuaris.append(nou_usuari)
return jsonify(nou_usuari), 201Exemple de Resposta JSON
Interacció amb Bases de Dades
Per interactuar amb bases de dades, podem utilitzar SQLAlchemy, una biblioteca ORM (Object-Relational Mapping) per a Python.
Instal·lació de SQLAlchemy
Configuració de SQLAlchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///usuaris.db'
db = SQLAlchemy(app)
class Usuari(db.Model):
id = db.Column(db.Integer, primary_key=True)
nom = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
db.create_all()Autenticació i Autorització
Per a la seguretat de les nostres APIs, podem utilitzar Flask-JWT-Extended per gestionar l'autenticació i l'autorització.
Instal·lació de Flask-JWT-Extended
Configuració de Flask-JWT-Extended
from flask import Flask, jsonify, request
from flask_jwt_extended import JWTManager, create_access_token, jwt_required
app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'super-secret' # Canvia això per una clau secreta segura
jwt = JWTManager(app)
@app.route('/login', methods=['POST'])
def login():
if not request.is_json:
return jsonify({"msg": "Missing JSON in request"}), 400
username = request.json.get('username', None)
password = request.json.get('password', None)
if username != 'test' or password != 'test':
return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token), 200
@app.route('/protegida', methods=['GET'])
@jwt_required()
def protegida():
return jsonify(logged_in_as=request.json.get('username')), 200
if __name__ == '__main__':
app.run(debug=True)Exemples Pràctics
Exemple Complet
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import JWTManager, create_access_token, jwt_required
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///usuaris.db'
app.config['JWT_SECRET_KEY'] = 'super-secret'
db = SQLAlchemy(app)
jwt = JWTManager(app)
class Usuari(db.Model):
id = db.Column(db.Integer, primary_key=True)
nom = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
db.create_all()
@app.route('/usuaris', methods=['GET'])
def obtenir_usuaris():
usuaris = Usuari.query.all()
return jsonify([{'id': u.id, 'nom': u.nom, 'email': u.email} for u in usuaris])
@app.route('/usuaris/<int:id>', methods=['GET'])
def obtenir_usuari(id):
usuari = Usuari.query.get_or_404(id)
return jsonify({'id': usuari.id, 'nom': usuari.nom, 'email': usuari.email})
@app.route('/usuaris', methods=['POST'])
def crear_usuari():
nou_usuari = request.get_json()
usuari = Usuari(nom=nou_usuari['nom'], email=nou_usuari['email'])
db.session.add(usuari)
db.session.commit()
return jsonify({'id': usuari.id, 'nom': usuari.nom, 'email': usuari.email}), 201
@app.route('/login', methods=['POST'])
def login():
if not request.is_json:
return jsonify({"msg": "Missing JSON in request"}), 400
username = request.json.get('username', None)
password = request.json.get('password', None)
if username != 'test' or password != 'test':
return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token), 200
@app.route('/protegida', methods=['GET'])
@jwt_required()
def protegida():
return jsonify(logged_in_as=request.json.get('username')), 200
if __name__ == '__main__':
app.run(debug=True)Exercicis Pràctics
-
Crear una API per gestionar productes:
- Crear una base de dades per emmagatzemar productes amb camps com
id,nom,preuiquantitat. - Implementar rutes per obtenir tots els productes, obtenir un producte per ID, crear un nou producte, actualitzar un producte existent i eliminar un producte.
- Crear una base de dades per emmagatzemar productes amb camps com
-
Afegir autenticació a l'API de productes:
- Utilitzar Flask-JWT-Extended per protegir les rutes de creació, actualització i eliminació de productes.
- Implementar una ruta de registre per permetre als usuaris crear comptes i una ruta de login per obtenir un token JWT.
Solucions
Solució a l'Exercici 1:
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///productes.db'
db = SQLAlchemy(app)
class Producte(db.Model):
id = db.Column(db.Integer, primary_key=True)
nom = db.Column(db.String(80), nullable=False)
preu = db.Column(db.Float, nullable=False)
quantitat = db.Column(db.Integer, nullable=False)
db.create_all()
@app.route('/productes', methods=['GET'])
def obtenir_productes():
productes = Producte.query.all()
return jsonify([{'id': p.id, 'nom': p.nom, 'preu': p.preu, 'quantitat': p.quantitat} for p in productes])
@app.route('/productes/<int:id>', methods=['GET'])
def obtenir_producte(id):
producte = Producte.query.get_or_404(id)
return jsonify({'id': producte.id, 'nom': producte.nom, 'preu': producte.preu, 'quantitat': producte.quantitat})
@app.route('/productes', methods=['POST'])
def crear_producte():
nou_producte = request.get_json()
producte = Producte(nom=nou_producte['nom'], preu=nou_producte['preu'], quantitat=nou_producte['quantitat'])
db.session.add(producte)
db.session.commit()
return jsonify({'id': producte.id, 'nom': producte.nom, 'preu': producte.preu, 'quantitat': producte.quantitat}), 201
@app.route('/productes/<int:id>', methods=['PUT'])
def actualitzar_producte(id):
producte = Producte.query.get_or_404(id)
dades = request.get_json()
producte.nom = dades.get('nom', producte.nom)
producte.preu = dades.get('preu', producte.preu)
producte.quantitat = dades.get('quantitat', producte.quantitat)
db.session.commit()
return jsonify({'id': producte.id, 'nom': producte.nom, 'preu': producte.preu, 'quantitat': producte.quantitat})
@app.route('/productes/<int:id>', methods=['DELETE'])
def eliminar_producte(id):
producte = Producte.query.get_or_404(id)
db.session.delete(producte)
db.session.commit()
return ('', 204)
if __name__ == '__main__':
app.run(debug=True)Solució a l'Exercici 2:
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import JWTManager, create_access_token, jwt_required
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///productes.db'
app.config['JWT_SECRET_KEY'] = 'super-secret'
db = SQLAlchemy(app)
jwt = JWTManager(app)
class Usuari(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
class Producte(db.Model):
id = db.Column(db.Integer, primary_key=True)
nom = db.Column(db.String(80), nullable=False)
preu = db.Column(db.Float, nullable=False)
quantitat = db.Column(db.Integer, nullable=False)
db.create_all()
@app.route('/registre', methods=['POST'])
def registre():
dades = request.get_json()
nou_usuari = Usuari(username=dades['username'], password=dades['password'])
db.session.add(nou_usuari)
db.session.commit()
return jsonify({"msg": "Usuari registrat correctament"}), 201
@app.route('/login', methods=['POST'])
def login():
dades = request.get_json()
usuari = Usuari.query.filter_by(username=dades['username']).first()
if usuari and usuari.password == dades['password']:
access_token = create_access_token(identity=usuari.username)
return jsonify(access_token=access_token), 200
return jsonify({"msg": "Nom d'usuari o contrasenya incorrectes"}), 401
@app.route('/productes', methods=['GET'])
def obtenir_productes():
productes = Producte.query.all()
return jsonify([{'id': p.id, 'nom': p.nom, 'preu': p.preu, 'quantitat': p.quantitat} for p in productes])
@app.route('/productes/<int:id>', methods=['GET'])
def obtenir_producte(id):
producte = Producte.query.get_or_404(id)
return jsonify({'id': producte.id, 'nom': producte.nom, 'preu': producte.preu, 'quantitat': producte.quantitat})
@app.route('/productes', methods=['POST'])
@jwt_required()
def crear_producte():
nou_producte = request.get_json()
producte = Producte(nom=nou_producte['nom'], preu=nou_producte['preu'], quantitat=nou_producte['quantitat'])
db.session.add(producte)
db.session.commit()
return jsonify({'id': producte.id, 'nom': producte.nom, 'preu': producte.preu, 'quantitat': producte.quantitat}), 201
@app.route('/productes/<int:id>', methods=['PUT'])
@jwt_required()
def actualitzar_producte(id):
producte = Producte.query.get_or_404(id)
dades = request.get_json()
producte.nom = dades.get('nom', producte.nom)
producte.preu = dades.get('preu', producte.preu)
producte.quantitat = dades.get('quantitat', producte.quantitat)
db.session.commit()
return jsonify({'id': producte.id, 'nom': producte.nom, 'preu': producte.preu, 'quantitat': producte.quantitat})
@app.route('/productes/<int:id>', methods=['DELETE'])
@jwt_required()
def eliminar_producte(id):
producte = Producte.query.get_or_404(id)
db.session.delete(producte)
db.session.commit()
return ('', 204)
if __name__ == '__main__':
app.run(debug=True)Conclusió
En aquest tema, hem après a construir APIs REST utilitzant Flask. Hem cobert des de la configuració inicial fins a la gestió de peticions i respostes, la interacció amb bases de dades, i l'autenticació i autorització. Amb aquests coneixements, estàs preparat per crear APIs REST robustes i segures amb Flask.
Curs de Programació en Python
Mòdul 1: Introducció a Python
- Introducció a Python
- Configuració de l'Entorn de Desenvolupament
- Sintaxi de Python i Tipus de Dades Bàsics
- Variables i Constants
- Entrada i Sortida Bàsiques
Mòdul 2: Estructures de Control
Mòdul 3: Funcions i Mòduls
- Definició de Funcions
- Arguments de Funció
- Funcions Lambda
- Mòduls i Paquets
- Visió General de la Biblioteca Estàndard
Mòdul 4: Estructures de Dades
Mòdul 5: Programació Orientada a Objectes
Mòdul 6: Gestió de Fitxers
- Lectura i Escriptura de Fitxers
- Treballant amb Fitxers CSV
- Gestió de Dades JSON
- Operacions amb Fitxers i Directoris
Mòdul 7: Gestió d'Errors i Excepcions
Mòdul 8: Temes Avançats
- Decoradors
- Generadors
- Gestors de Context
- Concurrència: Fils i Processos
- Asyncio per a Programació Asíncrona
Mòdul 9: Proves i Depuració
- Introducció a les Proves
- Proves Unitàries amb unittest
- Desenvolupament Guiat per Proves
- Tècniques de Depuració
- Ús de pdb per a la Depuració
Mòdul 10: Desenvolupament Web amb Python
- Introducció al Desenvolupament Web
- Conceptes Bàsics del Framework Flask
- Construcció d'APIs REST amb Flask
- Introducció a Django
- Construcció d'Aplicacions Web amb Django
Mòdul 11: Ciència de Dades amb Python
- Introducció a la Ciència de Dades
- NumPy per al Càlcul Numèric
- Pandas per a la Manipulació de Dades
- Matplotlib per a la Visualització de Dades
- Introducció al Machine Learning amb scikit-learn
