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:

pip install Flask

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ó:

python app.py

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

@app.route('/usuaris', methods=['GET'])
def obtenir_usuaris():
    return jsonify(usuaris)

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

pip install Flask-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

pip install 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

  1. Crear una API per gestionar productes:

    • Crear una base de dades per emmagatzemar productes amb camps com id, nom, preu i quantitat.
    • Implementar rutes per obtenir tots els productes, obtenir un producte per ID, crear un nou producte, actualitzar un producte existent i eliminar un producte.
  2. 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

Mòdul 2: Estructures de Control

Mòdul 3: Funcions i Mòduls

Mòdul 4: Estructures de Dades

Mòdul 5: Programació Orientada a Objectes

Mòdul 6: Gestió de Fitxers

Mòdul 7: Gestió d'Errors i Excepcions

Mòdul 8: Temes Avançats

Mòdul 9: Proves i Depuració

Mòdul 10: Desenvolupament Web amb Python

Mòdul 11: Ciència de Dades amb Python

Mòdul 12: Projecte Final

© Copyright 2024. Tots els drets reservats