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': '[email protected]'}, {'id': 2, 'nom': 'Maria', 'email': '[email protected]'} ] @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), 201
Exemple 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
,preu
iquantitat
. - 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