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.