# Créer un serveur Python simple
+++ sommaire
Dès la première NSI, les apprenants doivent assimiler les concepts gravitant autour de l'architecture client/serveur.
Comme nous ne rentrons pas dans de la gestion avancée de serveur, il n'est pas nécessaire de devoir installer de grosses applications telles que Apache, nginx ou encore nodejs.
C'est la raison pour laquelle je choisis Python, langage phare du programme du lycée et déjà connu des apprenants, qui permet en quelques lignes de mettre en route de très simples serveurs.
Les codes ci-dessous sont très largement commentés/loggué, ils peuvent donc être autant destinés aux enseignants qu'aux apprenants.
## Serveur 'GET'
Le serveur 'GET' est le plus simple et ne gère que les requêtes de type 'GET'.
Il vous suffit de créer un dossier nommé, par exemple, 'serveurPython' et de copier/coller le code ci-dessous dans un fichier nommé, par exemple, serveurMinimaliste.py que vous placez dans votre nouveau dossier.
+++image "lienImage" classes
+++draggableTextBloc "text" classes
```python
# Contenu du fichier 'serveurMinimaliste.py'
# Fait avec python v3.8
# Depuis le module 'http.server', nous importons les sous modules HTTPServer et SimpleHTTPRequestHandler
# HTTPServer : c'est le coeur du serveur
# SimpleHTTPRequestHandler : explique au serveur comment gérer les requêtes de type "GET"
from http.server import HTTPServer, SimpleHTTPRequestHandler
# Ici :
# - nous instancions notre serveur dans la variable 'httpd' à l'aide du constructeur 'HTTPServer'
# - nous expliquons que le serveur ouvrir son port numéro 8080
# - et nous expliquons quel comportement doit avoir le serveur en soumettant dans le constructeur 'SimpleHTTPRequestHandler'
# Et souvenez vous, comme écrit plus haut en commentaire, 'SimpleHTTPRequestHandler' ne gère que des requêtes de type "GET"
httpd = HTTPServer(('localhost', 8080), SimpleHTTPRequestHandler)
# Ensuite nous démarrons notre serveur qui tournera 'pour toujours' ('serve forever') jusqu'à ce qu'on décide de l'arréter
print("serveur : je démarre, je me met à l'écoute d'éventuelles requêtes ! \n")
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
# Si jamais nous stoppons 'serveurMinimaliste.py', nous cloturons proprement le serveur
httpd.server_close()
logging.info('Serveur cloturé !...\n')
```
Maintenant vous devez exécuter votre fichier 'serverMinimaliste.py', soit :
- depuis votre IDE, comme spyder3 par exemple
![spyder3](https://i.ibb.co/Ptkb4w0/Screenshot-from-2021-01-06-20-39-34.jpg)
- ou en ligne de commande, comme sous linux par exemple (commande `python3.8 serveurMinimaliste.py`)
![commande linux](https://i.ibb.co/XW1DB6x/Screenshot-from-2021-01-06-20-34-51.png)
**Votre serveur est démarré !** :ok_hand:
ps : si vous avez une erreur, il est possible que le port actuel soit déjà utilisé par une autre application de votre ordinateur. Il vous faut donc simplement définir un autre port d'écoute dans le code (privilégiez un chiffre entre 20000 et 40000)
Pour le tester votre serveur, c'est très simple :
- ouvrez dans votre navigateur préféré un nouvel onglet
- dans cet onglet, renseignez l'url 'localhost:8080' (où 8080 est le numéro de port que vous avez défini dans le code du serveur)
- valider !
Votre page se charge et vous devriez voir quelque chose ressemblant à ceci :
![screenshot navigateur](https://i.ibb.co/98wxwB7/Screenshot-from-2021-01-06-20-30-09.png)
**Bravo !** votre serveur minimaliste fonctionne parfaitement !
Le comportement de ce serveur minimaliste (indiqué grâce à `SimpleHTTPRequestHandler`) est simplement d'afficher les fichiers dans le dossier dans lequel il se trouve. Il permet également d'afficher le contenu de ces fichiers lorsqu'on clique dessus.
## Serveur 'GET/POST'
Le Serveur 'GET' est très simple à mettre en place mais devient rapidement limité. Tandis que les requêtes type 'GET' ne doivent que permettre au client de récupérer des ressources auprès du serveur, les requêtes type 'POST' permettent au client d'envoyer des données au serveur entraînant généralement un **changement d'état** de ce dernier.
Par exemple, chaque fois que vous créez un compte sur un site Internet (email + mot de passe), une requête POST contenant votre mail et votre mot de passe est envoyée au serveur. Ce serveur qui ne connaissait pas votre email ni mot de passe a maintenant **en mémoire** ces informations. On dit que le serveur a **changé d'état**.
Nous restons tout de même dans un cas simple ici, notre serveur ne gardera aucune donnée en mémoire (nous voyons cela au chapitre des bases de données en Terminale). Ici, il notifie juste qu'il a bien reçu de la part du client la requête 'POST' avec ses données.
**Nous devrons donc créer un formulaire HTML envoyant une réponse de type POST au serveur** (voir code ci-dessous)
Pour le serveur 'GET', nous avons utilisé le sous module `SimpleHTTPRequestHandler`. Pour pouvoir aller au delà des simples requêtes 'GET', nous devons créer notre propre `Handler` et expliquer dedans comment le serveur doit gérer les requêtes de type 'GET' et de type 'POST' provenant des clients.
Voici le code du serveur 'GET/POST'
```python
# fait en python v3.8
from http.server import BaseHTTPRequestHandler, HTTPServer
# Notre handler doit être créé à travers une classe
# 'class' est un terme employé dans la Programmation Orientée Objet (POO), thème surtout abordé en terminale
# Si c'est la première fois que vous voyez le terme 'class', dites vous simplement qu'une 'classe' est une variable
# améliorée dans laquelle nous pouvons enregistrer plusieurs données et également des fonctions (qu'on appelle méthode
# en POO)
class monServerHandlerAMoi(BaseHTTPRequestHandler):
# notre classe 'monServerHandlerAMoi' doit suivre les règles définies dans 'BaseHTTPRequestHandler', ce qui implique
# que nous devons créer et renseigner les méthodes '_set_response', 'do_GET' et 'do_POST'
def _set_response(self): # Notre serveur ici 'construit' les en-têtes de ses réponses :
self.send_response(200) # - en utilisant le code http 200 pour dire au client que tout s'est bien passé
self.send_header('Content-type', 'text/html') # - en expliquant que sa réponse est un fichier de type 'html'
self.end_headers()
# Nous expliquons à notre serveur dans cette méthode comment il doit réagir si on le sollicite avec une requête de type 'GET'
def do_GET(self):
clientInformations = str(self.headers) # va servir pour les print juste en dessous
print("SERVEUR : [GET]")
print(f" - j'ai reçu une requête de type GET de la part du client")
print(f" - de cette requête GET, je reçois automatiquement les informations suivantes : \n{clientInformations}")
print(" - je construis maintenant une page HTML que j'enverrai ensuite au client...attention c'est très rapide !")
# Notre serveur lit dans son dossier un fichier nommé 'ressource.html', vous devez créer ce fichier.
# Il peut contenir du simple code HTML. Ici, pour tester si les requêtes 'POST' fonctionnent bien, vous
# devez créer un formulaire de type 'POST'
serverResponseBodyHTML = open('ressource.html').read()
print(f" - FINI ! j'ai construit la page HTML de mon côté (côté serveur donc) et je l'envoie au client\n\n")
# Il ne reste plus qu'à demander au serveur de construire la page avec le contenu de `serverResponseBodyHTML`
self._set_response()
self.wfile.write(serverResponseBodyHTML.encode("utf-8"))
# Nous expliquons à notre serveur dans cette méthode comment il doit réagir si on le sollicite avec une requête de type 'POST'
def do_POST(self):
# Le serveur vient de recevoir une requête de type 'POST'. Nous capturons dans la variable 'postData' les données
# envoyées par le client dans le formulaire 'ressource.html' qu'il a rempli
postData = self.rfile.read(int(self.headers['Content-Length'])).decode('utf-8')
print("SERVEUR : [POST]")
print(f" - j'ai reçu une requête de type POST de la part du client")
print(f" - cette requête POST contient des données de formulaire, ces données sont : \n{postData}\n")
print(f" - je construis maintenant une page HTML contenant un simple message de bonne réception du formulaire complété et je l'envoie au client")
# Le serveur affiche dans la page envoyée au client que tout s'est bien passé !
simpleMessage = "Le message a bien été transmis ! le contenu est visible sur le serveur..."
# Il ne reste plus qu'à demander au serveur de construire la page avec le contenu de `simpleMessage`
self._set_response()
self.wfile.write(simpleMessage.encode('utf-8'))
# Le code ci-dessous est très similaire à celui utilisé pour le serveur 'GET' excepté qu'on utilise un handler `monServerHandlerAMoi` que nous définissons plus haut
server_address = ('localhost', 8081)
httpd = HTTPServer(server_address, monServerHandlerAMoi)
print("serveur : je démarre, je me met à l'écoute d'éventuelles requêtes ! \n")
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
# Si jamais nous stoppons 'serveurMinimaliste.py', nous cloturons proprement le serveur
httpd.server_close()
logging.info('Serveur cloturé !...\n')
```
Tout comme pour le serveur 'GET', il vous suffit :
- de copier ce code ci-dessus dans un fichier nommé, par exemple, serverGetPost.py
- d'exécuter ce fichier (IDE, ligne de commande, ...) afin de démarrer le serveur
- d'accéder à l'url [http://localhost:8081](http://localhost:8081) depuis votre navigateur
Il vous faut penser à créer un formulaire html de type 'POST' comme indiqué dans les commentaires du code du serveur 'GET/POST'. Il ne reste plus qu'à remplir ce formulaire depuis [http://localhost:8081/ressource.html](http://localhost:8081/ressource.html) et de le soumettre pour voir que le serveur envoie en retour la page de bonne réception du formulaire complété !
![frodon](https://i.ibb.co/cX2qPrv/tenor.gif)