Écrire des microservices en Python – 4

Dans Cette quatrième partie Nous définirons une (simple) application et commencerons à ajouter des API REST.

L’exemple en question est dérivé d’une série de trois articles de Bill Ward, publié sure DZONE. Nous avons beaucoup travaillé sur les détails pour le rendre plus complet. Il s’agit d’une très simple bibliothèque, consistant en un ensemble de livres stockes en un fichier de texte, avec deux seuls champs d’informations pour chaque livre : L’auteur du livre et le titre.

L’exemple que nous proposerons n’est pas “propre” ni complet. Nos API vont avoir en retour à la fois des données en format JSON à la fois du HTML. Mais elle répondront à des requêtes de type GET, POST et DELETE, et elles essayeront de donner des codes de réponses correctes.

Tous les fichiers du code source sont disponible ici.

Les API :

Chaque API répondra à une requête bien spécifique, en étendant tornado.web.RequestHandler.

Notre application utilisera deux classes de base : La classe Books (la bibliothèque) et la classe BooksFile (pour garantir la persistance entre sessions). Le fichier de stockage sera un simple fichier de texte, nommé « mybooksfile.txt ». Nous ne nous référerons jamais à ce fichier directement, tout accès seront encapsulé dans les classes.

Dans le fichier api.py :

def make_app():
    return tornado.web.Application([
        (r"/books", MainHandler),                                  
        (r"/books/info", InfoHandler),                             
        (r"/books/addbook", AddHandler, dict(books = myBooks)),
        (r"/books/delbook", DelHandler, dict(books = myBooks)),
        (r"/books/listbooks", GetHandler, dict(books = myBooks)),
        (r"/books/bookdetails", DetailsHandler, dict(books = myBooks)),
        (r"/", ErrorHandler)
    ])

API 1 : MainHandler :

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("<h1>Book Microservice v1</h1>")

Cette classe gère la methode « GET » pour l’URL http://localhost/books et son retour est un fragment de HTML. Pour la simplicité de cette classe, elle se trouve dans le fichier api.API 2 : InfoHandler:##Cette classe se trouve dans un fichier à part, et réponde aux requêtes de type « GET » sur le URL http://localhost/books/info

import tornado.web
import book
import json
import asyncio 

class InfoHandler(tornado.web.RequestHandler):
  
    async def get(self):
        message = """<html>
        <head></head>
        <body><h2>Info API</h2>
        """      
        message = message + "<hr><p> <b>http://localhost:8888/books</b> - \
                                              Microservice Root </p><p>GET - Returns HTML</p>"
        message = message + "<hr><p> <b>http://localhost:8888/books/info</b> - \
                                              Api Information Page (this page) </p><p>GET - Returns HTML</p>"
        message = message + "<hr><p> <b>http://localhost:8888/books/addbook?title=\"Book Title\"&author=\"Book Author\"</b>\
                                              - Api to add a book. Does not accept duplicates </p><p>POST - Returns JSON</p>"
        message = message + "<hr><p> <b>http://localhost:8888/books/delbook?title=\"Book Title to delete\"</b>\
                                              - Api to delete a book </p><p>DELETE - Returns simple text</p>"
        message = message + "<hr><p> <b>http://localhost:8888/books/listbooks</b> -\
                                              Api to list all books </p><p>GET - Returns JSON</p>"
        message = message + "<hr><p> <b>http://localhost:8888/books/listbooks?author=\"Auhtor\"</b>\
                                               - Api to list all books from author </p><p>GET - Returns JSON</p>"                 
        message = message + "<hr><p> <b>http://localhost:8888/books/bookdetails?title=\"Title\"</b>\
                                             - Api to produce HTML for the book with title </p><p>GET Returns HTML page</p>"                         
        message = message + """</body> </html>"""   
        
        self.set_status(200)
        self.write(message)

Elle donne, en réponse une page HTML de documentations de toutes les API qu’on définira.

API 3 : AddBook:

AddBook nous permets d’ajouter un livre à notre bibliothèque. Il s’agit d’une requête de type « POST » et pourtant il n’est pas possible d’utiliser un navigateur web normale pour la tester. Pour faire ça, nous pouvons utiliser un logiciel spécifique comme Postman ou une plus simple extension du navigateur (Rester fait ça très bien en chrome par exemple).

import tornado.web
import book
import json

class AddHandler(tornado.web.RequestHandler):
    def initialize(self, books):
        self.books = books
        
    def post(self):
        
        title = self.get_argument('title').strip()
        author = self.get_argument('author').strip()
        
        result = self.books.add_book(title, author)
        
        if result == False: 
            self.set_status(200)
            self.write(json.dumps("Book already Exists"))
        else :
            self.set_status(201)
            self.write(json.dumps(result))

Ici il faut noter que nous étendons la méthode « post », et aussi que nous faisons un effort de retourner un code de réponse correct: 201 si l’addition du livre s’est bien passé et 200 avec un message « jsonifié » pour dire que le livre existe déjà dans notre bibliothèque.

Comme déjà dit, le reste du code est disponible sur github.