POST-vervoer met Python REST-server
- October 27, 2017
- 0
Het komt vaak voor dat je een eenvoudige verbindingsbrug nodig hebt om naam:waarde paren over te brengen tussen verschillende applicaties. Dit kunnen besturingscommando’s zijn, zoals [vooruit:3],[rechts:6] of uitgelezen waarden van een meetinstrument. Zender en ontvanger zitten niet op dezelfde machine. Een eenvoudige oplossing hiervoor is het toepassen van een REST-server. Dit is een server binnen een netwerkarchitectuur, waarbij geen sessiestatus wordt bijgehouden. Elke gegevensuitwisseling staat op zichzelf: een HTTP-netwerk in zijn simpelste vorm.
Voor een REST-server is het niet nodig om een complex framework als Django of Flask in te zetten. Python heeft hiervoor van zichzelf al de benodigde middelen. Gebruik daarvan biedt voordelen als je beschikt over beperkte opslagruimte. Bovendien hoef je je dan niet te verdiepen in de eigenaardigheden van een framework. Aanvullende modules, die je eerst moet inladen, zijn voor de REST-server niet nodig.
Hieronder vind je een voorbeeld van een invoerprogramma waarmee de ingevoerde gegevens naar een webserver verstuurd worden. Het invoerprogramma en de webserver zijn beide geschreven in Python. De metafoor van een Gegevensbron en een Datalogger is hier als toepassingsvoorbeeld gekozen. Voor de beschreven broncode heb ik Python 3.4 gebruikt (hoog tijd om Python 2.x vaarwel te zeggen). Voor de communicatie tussen Gegevensbron en HOST volstaat een HTTP-verbinding (HTTP is een standaard voor internetcommunicatie).
Datalogger
Het programma voor de datalogger is opgebouwd in drie lagen (zie Listing 1). Hiermee voorkom je dat de broncode rommelig wordt en kun je ook na een jaar nog zien waar het overgaat. Het is daarbij een goed gebruik om klasse definities van een documentatieregel te voorzien.
Laag 1
Om het programma direct vanuit een terminal te kunnen aanroepen, gebruik je op de eerste regel een “shebang”. In ons geval is de shebang:
#!/usr/bin/env python3
Met de aanduiding python3 wordt automatisch de hoogste versie gekozen. Op dit moment is dat voor de meeste Linux-distributies python3.4. Verder vind je hier een lijst met imports van de gebruikte modules. Dit zijn allemaal standaard Python3 modules. Bijzonder is de coderegel om het hostadres (HOSTIP) te vinden. Deze code heb ik door slim googelen gevonden. Het werkt, maar vraag me niet hoe… Het hostadres is nodig om de webserver van buitenaf bereikbaar te maken. Gebruik van ‘localhost’ werkt alleen binnen de eigen omgeving en met HOSTIP is de server meteen ook geschikt voor gebruik binnen een container. Het POORT-nummer is willekeurig gekozen. Zolang het maar niet 80 of 8080 is, want deze poortnummers zijn vaak al in gebruik voor toegang tot openbare servers.
Laag 2
De uit te voeren acties bij ontvangst van gegevens zijn vastgelegd in een klasse “Postbezorger”. De Postbezorger klasse is geërfd van een bestaande Python klasse “BaseHTTPRequestHandler”. Het is gebruikelijk om een beschrijving van wat de klasse doet direct onder de klasse aanroep te schrijven. Dit kan in een documentatieblok, dat begint met “”” en eindigt met “””. Alles daartussen wordt bij de executie van de code genegeerd. Voor het gegevenstransport kun je kiezen tussen GET en POST. POST is niet zichtbaar in de URL-balk van een browser. Voor alledaags gebruik (met of zonder browser) is het uitlezen van een URL-balk niet van belang en gebruik je de HTTP POST-functie. Verder valt op dat CGI gebruikt is. CGI (Common Gateway Interface) is een internettechniek, die het mogelijk maakt om met gebruik van een script gegevens uit te wisselen met een webserver.
De server verwacht een HTML header met de gegevens voor “temperatuur” en “luchtdruk”. Je kunt deze header opvragen en uitsplitsen (parsen) met cgi.FieldStorage(). Voor controle verzend je daarna een antwoordbericht met “200”. Dat betekent in HTTP termen “goed ontvangen”. Voor het geval je een browser gebruikt om de gegevens te verzenden, is ook een ontvangstbevestiging in HTML opgenomen. Nadat de POST header uitgesplitst is, leg je de ontvangen gegevens vast in de file “opslag” met een ‘;’ als scheidingsteken tussen de twee gegevens en ‘end of line’ (\n) per invoer. Dit type file kun je gemakkelijk uitlezen met een spreadsheetprogramma. Een UTC-tijdstempel wordt automatisch mee weggeschreven. De opslag file wordt bij de eerste keer starten van de webserver aangemaakt als deze nog niet bestaat. Als “opslag” al bestaat, worden de ontvangen gegevens achteraan toegevoegd.
Laag 3
Het hoofdbestanddeel is ook het kortste deel. Het ‘if __main__ …’ statement is typisch Python en controleert of de broncode direct wordt gebruikt of afkomstig is van een geïmporteerde module. In dit geval is dat direct gebruik. Je moet alleen nog een REST-server definiëren met het host IP-adres, poortnummer en de aan te roepen uitvoeringsklasse invullen en klaar is kees. Zoals je kunt zien is een socketserver van het model TCP gebruikt. De aangeroepen uitvoeringsklasse “PostBezorger” is in de 2e schijf aangemaakt. Het printstatement kun je ook weglaten, maar voor een eerste test maakt het je gemakkelijk om het IP-adres en poortnummer te vinden. Dit adres en poortnummer kun je overnemen in de zender van de gegevensbron. Je kunt de server stoppen met Ctrl-C in de terminal.
Gegevensbron
De verzendapplicatie is, zo mogelijk, nog simpeler (zie Listing 2).
Let op dat je het juiste server IP-adres en poortnummer overneemt na het starten van de REST-server. Het server HOST-adres wordt bij het opstarten van je computer automatisch toegewezen door de DHCP-server en kan per keer verschillen. Voor het aanmaken en bewaren van naam:waarde paren is een Python dictionary een zeer geschikt instrument. Een dictionary kun je zien als een telefoonlijst met items als “piet”: 0612345678,”kees”: 068765421, “…”: …. Een lege dictionary wordt aangemaakt met ‘data = {}’ waarbij de accolades aangeven dat het een dictionary betreft.
While True loop
Al het werk wordt binnen een “while True” loop gedaan. De “while True” loop vraagt net zolang om invoer, totdat je dit beëindigt met Ctrl-C. Je maakt naam:waarde paren aan in de dictionary met een “input(“tekstlabel”)” commando. Voor de verbinding met de server gebruik je de httplib2 module. De httplib2 module heeft alle noodzakelijke functies voor communicatie met een REST-server. De code bestaat slechts uit twee regels:
h = httplib2.Http(‘.cache’, timeout = 1)
Dit reserveert de tijdelijke communicatie gegevensopslagruimte en één seconde timeout.
resp, content = h.request(…)
Dit zorgt voor het verzenden van “form.xtml” berichten. De extensie .xtml geeft aan dat het hier een typisch telecommunicatieprotocol betreft. Om dit protocol te ondersteunen is “urlencode” uitgevonden, ook wel percent-code genoemd. Het “resp” (response) deel vangt het antwoord van de server op.
Opmerking: Als bij jou httplib2 niet als standaardmodule opgenomen is, dan kun je deze eenvoudig met onderstaande listing alsnog installeren.
sudo python3 -m pip install httplib2
Met N = resp.status wordt het statusdeel van de antwoordstring opgehaald. Als de REST-server een correct form heeft ontvangen, geeft print(str(N)) het getal 200.
Tenslotte
Moeten we alleen nog de server en de verzendapplicatie elk in een aparte terminal starten. Hiervoor moet je eerst beide programma’s met chmod 777 <programmanaam> executable maken. In de terminal binnen de directory start je het programma met ./<programmanaam>. Na het invullen van een aantal gegevens kun je de file “opslag” uitlezen met bijvoorbeeld LibreOffice Calc.