Mon API Rest en Python avec Falcon (2/2)

Rappel des besoins

Comme nous le présentions dans le précédent article, il est question de mettre en place une API Rest avec Falcon, disponible pour:

  • Lister les interventions en cours
  • Ajouter une nouvelle demande d'intervention
  • Accéder au détail d'une intervention
  • Modifier une intervention
  • Supprimer une intervention

Note sur la modélisation: Falcon est bien entendu compatible avec une multitude de systèmes de gestion de base de données (PostgreSQL, MySQL, MongoDB, SQLite ...). Pour cet exemple, nous utiliserons  SQLite.

Installation de Falcon

Comme avec la plupart des bibliothèques Python, l'installation de Falcon ne pose pas de soucis majeur puisqu'un pip install falcon suffit pour débuter les développements.

L'installation de Gunicorn avec pip install gunicorn nous permettra d'instancier un serveur pour tester notre API.

Les prochains codes sont proposés afin de présenter les généralités de la construction d'API avec Falcon. Les nombreuses failles de sécurité font que ces exemples NE DOIVENT PAS ETRE MIS EN PRODUCTION.

Lister les interventions en cours

La première des choses que nous pouvons mettre en place c'est l'affichage de la liste des interventions.

  • url: /interventions/
  • Method: GET
  • Exemple de requête:

curl -X GET http://localhost:8000/interventions

def on_get(self, req, resp):        
    interventions = []        
    with self.con:            
    	self.cur = self.con.cursor()            
    	self.cur.execute('SELECT * FROM Interventions;')            
    	data = self.cur.fetchall()                     
    	for intervention in data:
        	interventions.append(intervention)        
    resp.status = falcon.HTTP_200           
    resp.body = json.dumps(interventions)

Etape par étape, nous créons une liste vide qui contiendra notre liste d'interventions. Une requête sql se charge d'aller chercher l'ensemble des entrées de la table "Interventions". Chaque résultat est placé dans notre liste puis retournée en json avec un code 200 qui indique la réussite d'une requête.

Dans la mesure ou nous n'avons pas encore enregistré d'intervention dans notre base de données, la liste retournée est vide.

Ajouter une nouvelle demande d'intervention

Utilisons désormais notre API pour insérer une nouvelle demande dans notre base de données.

  • url: /interventions/
  • Method: POST
  • Exemple de requête:

curl -d '{"title":"restart server", "description":"our main server has to be reboot"}' -H "Content-Type: application/json" -X POST http://localhost:8000/interventions

def on_post(self, req, resp):
    data = json.load(req.stream)
    title = data['title']
    description = data['description']
    with self.con:
    	self.cur = self.con.cursor()
    	self.con.execute('INSERT INTO Interventions (title, description) VALUES ("%s", "%s")' % (title, description))
    resp.status = falcon.HTTP_200

La première étape consiste à récupérer les paramètres passés dans la requête. Dans notre exemple, nous récupérons les données title et description. Ces 2 variables sont ensuite passées en paramètres de notre requête SQL d'insertion.

Le retour est un code 200 qui indique la réussite d'une requête.

Accéder à une intervention spécifique

Nous continuons notre cheminement avec l'accès à l'intervention que nous venons de créer.

  • url: /interventions/{{intervention_id}}
  • Method: GET
  • Exemple de requête:

curl -X GET http://localhost:8000/interventions/1

def on_get(self, req, resp, intervention_id):
	with self.con:
    	self.cur = self.con.cursor()
        self.cur.execute("SELECT * FROM Interventions where id=%s;" % intervention_id)
        data = self.cur.fetchone()
        if data:
        	resp.status = falcon.HTTP_200
            resp.body = json.dumps(data)
        else:
        	resp.status = falcon.HTTP_404

La méthode récupération de l'intervention est très similaire à la récupération de la liste des interventions que nous avons vu plus haut à l'exception du fait que l'on se base sur l'identifiant (intervention_id) passé en paramètre dans l'url. Cet identifiant est utilisé dans notre  requête SQL qui, si elle retourne des données, permet d'afficher les informations de l'intervention. Le cas échant, la requête retourne un code 404 ce qui signifie ici que l'identifiant est invalide et qu'il n'existe pas d'intervention associée.

Modifier une intervention

Dans l'hypothèse où les informations d'une intervention sont fausses, il doit être possible de pouvoir modifier son contenu.

  • url: /interventions/{{intervention_id}}
  • Method: PUT
  • Exemple de requête:  curl -X PUT -H "Content-Type: application/json" -d '{"title":"restart our main server","description":"Our main server has to be reboot this next friday"}' http://localhost:8000/interventions/6
def on_put(self, req, resp, intervention_id):
	data = json.load(req.stream)
    title = data['title']
    description = data['description']
    with self.con:
    	self.cur = self.con.cursor()
        self.cur.execute('UPDATE Interventions SET title = "%s", description = "%s" WHERE id = "%s"' % (title, description, intervention_id))
    resp.body = data['title']  
    resp.status = falcon.HTTP_200

Tout comme l'accès à l'intervention présenté juste avant, nous utilisons l'ID passé en paramètre de l'url dans notre requête SQL. Cet Update utilise les données envoyées dans la requête et récupérées par req.stream.

Le retour est un code 200 qui indique la réussite d'une requête.

Supprimer une intervention

Ce dernier exemple de notre cas nous permet de supprimer une intervention.

  • url: /interventions/{{intervention_id}}
  • Method: DELETE
  • Exemple de requête:  curl -X DELETE http://localhost:8000/interventions/6
def on_delete(self, req, resp, intervention_id):
	with self.con:
    	self.cur = self.con.cursor()
        self.cur.execute("DELETE FROM Interventions WHERE id=%s;" % intervention_id)
    resp.status = falcon.HTTP_200

Une nouvelle fois, le principe est d'utiliser l'ID de l'intervention passé en paramètre dans notre requête SQL. Une fois la suppression effectuée, un code 200 est retourné pour indiquer la réussite d'une requête.

Et après ?

Le code source est disponible ici. Les exemples présentés ici sont sous leur forme la plus simple pour être compris rapidement et facilement. En aucun cas ils ne devraient être présents sur une application en production avant d'être sécurisés. Il s'agit alors de corriger les failles qui permettent les injections SQL, il faudrait y ajouter un système d'authentification, ou encore utiliser l'abstraction permise par le framework pour gagner en lisibilité et en maintenabilité.

Vous êtes développeur et à la recherche d'un emploi ? Faites nous parvenir une version corrigée de cette API et candidatez à l'un de nos poste de consultant Python !