Salta el contingut

4. Servidors amb Flask

Primer programa en Flask

Per començar crearem un nou entorn per a tota la programació en flask

Preparant l'entorn
Text Only
1
2
3
conda create -n flask python=3.10
conda list env
conda activate flask

a continuació:

  1. Instal·lem flask amb conda install flask
  2. Creem el programa principal, amb nom app.py (per exemple)
  3. (Linux) Exportem el nom de l'aplicació export FLASK_APP=app.py
  4. (Windows) set FLASK_APP=app.py
  5. S'executa amb flask run

Un cop executat, ens apareixerà una cosa com:

Text Only
1
2
3
4
 * Serving Flask app 'app.py'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000

El que ens diu que tenim un servidor escoltant al port 5000, i podem obrir-lo al navegador. Vegem el primer programa com quedarà

Hola món amb Flask
Python
1
2
3
4
5
6
7
8
9
from flask import Flask
app=Flask(__name__)

@app.route('/')
def hola_mon():
    return "Hola món"

if __name__=="__main__":
    app.run()   # localhost at 5000   

A comentar:

  • A la primera línia importem el framework, creant a continuació amb el constructor el nostre servidor.
  • Es defineix un primer recurs a oferir (la / o també el recurs arrel) dins dels nostre servidor, que ens retornarà un string amb la salutació Hola món.
  • La ruta s'indica amb l'anotació @app.route(ruta), i si no es posa res més s'enten que la petició és GET
  • Aquesta servidor serà llançat desde el programa principal (aquell que s'anomena main)

Llavors ja podem accedit des del navegador a dit recurs: Primer programa amb Flask

Mapejant noves rutes i mètodes

Si volem podeer mapejar altre tipus de peticions, hem d'indicar quin mètode és. Podem fer-ho indicant-ho com a paràmetre del app.route(). Ademés hem d'extreure el JSON que ens ha enviat el client dins del cos, cosa que podem fer amb el mètode request.get_json(), que ens retorna el json del body de la petició dins d'un diccionari python.

Primer exemple de POST
Python
1
2
3
4
5
6
7
8
9
@app.route("/saluda",methods=["POST"])
def saluda():
    body=request.get_json();
    resp="Hola <b>" + body["name"]+"</b>"
    ret={
        "resposta":resp
    }

    return jsonify(ret),201
  • Comentar que hem d'afegir noves importacions, amb la línia from flask import Flask, jsonify,request.
  • Ademés podem afegir a la resposta, el codi HTTP per a que el client sàpiga quin és l'estat de la seua petició.
Un POST més complexe i amb control d'errors
Python
@app.post("/suma")
def suma():
    body=request.get_json()
    if not all(param in body for param in ("x","y")):
        return "Error de format al body", 400
    x=body["x"]
    y=body["y"]
    suma=x+y
    ret={
        "suma":suma
    }

    return jsonify(ret),200

Ací observem que:

  • dins de l'anotació @app, en compte de la combinació route i method podem programar directament app.post, app.get (per defecte), app.delete, etc. Això facilita la lectura del nostre programa
  • Un cop recuperat el json que ens envia el client, podem comprovar els elements necessaris, a la condició de la línia 4. Cas de faltar-ne algun retornem un error 400 (petició mal formada)

Recuperant dades dels paràmetres o de la ruta

Com sabem hi han 3 maneres bàsiques de passar dades a la nostra API, que son mitjançant paràmetres, variables de ruta i al cos de la petició:

  • Paràmetres → un cop escrita la ruta, s'afig un símbol d'interrogació (?) i es confecciona una llista de paràmetres amb nom=valor. Per exemple localhost:5000/cliente?username=Pepe&password=1234. Per a aconseguir-ho els valors del paràmetres farem servir els mètode request.args.get(nom_paràmetre)
Recuperació de paràmetres
Python
1
2
3
4
5
6
7
# /cliente?username=Pepe&password=1234
@app.route('/cliente')
def get_cliente():
    username = request.args.get('username')
    password = request.args.get('password')

    return f'He demanat el client {username} & {password}'
  • Variables de ruta → dins de la propia ruta apareix un codi o valor que simbolitza una dada. Per exemple localhost:5000/cliente/12 on s'arreplegaria les dades del client de codi 12. Per a aconseguir-ho caldrà afegir a la funció que mapeja la ruta un paràmetre amb el mateix nom que podem dins de ladefinició de la ruta.
Recuperació de paràmetres
Python
1
2
3
4
# /cliente/12
@app.route('/cliente/<id_Cliente>')
def get_cliente_by_Id(id_Cliente):
    return f'He demanat el client {id_Cliente}'
  • Cos de la petició → les dades es passen al cos de la petició dins d'un JSON. Ja ho hem vist anteriorment, amb el mètode body=request.get_json()

Creant API's amb flask

Flask incopora unes llibreries a traves del paquet flask-restful per a facilitar la construcció de API. Per això necessitem:

  • conda install flask-restful
  • from flask_restful import Api,Resource
  • Api definirà una Api dins de la nostra app
  • Resource definirà cadascun dels recursos o rutes dins de la nostra API

La classe Resource és una interfície, que conté els mètodes post(),get(), delete(), etc... Si volem crear una API que atenga a dits mètodes, deurem crear una classe que implemente dita interfície, i implementar cadascun dels mètodes.

Per afegir desprès la ruta a cadascun dels mètodes anteriors, ho podem fer amb el màtode api.add_resource(classe, ruta).

Exemple de implementació de Resource
Python
class Sumar(Resource):
    def post(self):
        return "He fet un POST a add"
    def get(self):
        return "He fet un GET a add"
    def delete(self):
        return "He fet un DELETE a add"
    def put(self):
        return "He fet un PUT a add"

api.add_resource(Sumar, "/add")

Amb això hem definit els 4 mètodes (post, get, delete i put) per a la ruta /add

Projecte

Crear una xicoteta api en Flask que:

  • Tinga una petició de benvinguda a l´'index que retorne Hola seguit de la data actual
  • Una petició de /register que reb al cos un username, password i icono, i ho emmagatzeme en una BBDD mongoDB que haurem creat per a l'ocasió. El icono servirà per a l'avatar. Recorda de guardar el password encriptat (llibreria bcrypt). Hi haurà un contador de tokens, iniciat a 10, que son les consultes que pot fer-nos l'usuari. Retornarà un missatge "Hola nom, et queden 10 tokens"
  • Una petició de /login, que reb al cos el username i password. Cas d'estar tot be, retornarà un 200 OK i el avatar de l'usuari.
  • Una petició de /consulta, amb un username i password.
  • Cas d'estar tot be retorna un missatge de Consulta efectuada. Et qeden X tokens.
  • Cas de no quedar tokens un codi d'error i un missatge de Efectua recàrrega
  • Una petició de /refill, amb un username, admin_pw (un password de administrador) i amount, que augmentarà la quantitat de tokens. Retornarà:
  • Un 200 ok i un missatge amb la quantitat de tokens actualitzada
  • Un codi i missatge d'error si l'usuari no existeix o que el password d'administrador és incorrecte. Aquest password serà fixe (S3g1r0)
  • A mode de administració, implementar un GET /login que retorne tots els usuaris de la BBDD, passant-li com a paràmetre el password de l'adminstrador

Warning

Text Only
1
2
- Discutirem més endavant com protegir algunes rutes amb privilegis, ja que el refill no pot fer-ho tot el mon, sinò sols un usuari adminstrador.
- La part dels iconos deixa-la per al final, com ampliació de l'exercici, un cop ho tingues tota la funcionalitat bàsica implementada.