[PYTHON] Erstellen Sie einen (einfachen) REST-Server

Einführung

REST kann bequem für die Datenkommunikation zwischen dem Server und dem Client verwendet werden. In Bezug auf die Verwendung von REST gibt es Informationen zum Erstellen eines Clients in verschiedenen Sprachen und Frameworks. Auf der Serverseite gibt es jedoch Informationen wie das Erstellen mit "JsonServer" oder Apache + PHP als Mock, die leicht vorbereitet werden können. Dies ist jedoch eine Methode, um Unannehmlichkeiten zu akzeptieren oder Zeit und Mühe zu nehmen. (Es wird so weit sein, wie ich finden kann.)

Dieses Mal habe ich versucht, einen Server zu erstellen, der sich in der Mitte des oben genannten befindet, mit dem Sie Dienste frei festlegen und einfach mit einem Skript erstellen können. (Der Zweck besteht nicht darin, eine ausreichende Robustheit zu erfordern, um im WEB veröffentlicht zu werden, sondern einen eigenen Raspberry Pi-Dienst aufzubauen.)

Quellcode

Es ist ein Live-Ressourcencode.

restserver.py


#!/usr/bin/env python3

import http.server
import json
import threading
import sys,os
import time

class RestHandler(http.server.BaseHTTPRequestHandler):
    def do_OPTIONS(self):
        #Unterstützt Preflight-Anfragen
        print( "options" )
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
        self.send_header('Access-Control-Allow-Headers', '*')
        self.end_headers()

    def do_POST(self):
        print( "post" )
        local_path = self.path.strip("/").split("/")
        #Anfrage erhalten
        content_len  = int(self.headers.get("content-length"))
        body = json.loads(self.rfile.read(content_len).decode('utf-8'))

        #Antwortverarbeitung
        if( local_path[0] == "dat" ):
            if(os.path.getsize('./dat.json')):
              with open('./dat.json', 'r') as json_open:
                json_load = json.load(json_open)

              json_load.update(**body)
              json_wraite = json.dumps(json_load, sort_keys=False, indent=4, ensure_ascii=False)
            else:
              json_wraite = json.dumps(body, sort_keys=False, indent=4, ensure_ascii=False)

            with open('./dat.json', 'w') as json_open:
                json_open.write(json_wraite)

            self.send_response(200)
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
            self.send_header('Access-Control-Allow-Headers', '*')
            self.end_headers()
            return
        else:
            print( "no" )
            print( self.path )
            return

    def do_GET(self):
        print( "get" )
        local_path = self.path.strip("/").split("/")
        #Antwortverarbeitung
        if( local_path[0] == "dat" ):
            print( "dat" )
            if(os.path.getsize('./dat.json')):
              with open('./dat.json', 'r') as json_open:
                json_load = json.load(json_open)

            else:
              json_load = {}

            self.send_response(200)
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
            self.send_header('Access-Control-Allow-Headers', '*')
            self.send_header('Content-type', 'application/json;charset=utf-8')
            self.end_headers()
            body_json = json.dumps(json_load, sort_keys=False, indent=4, ensure_ascii=False) 
            self.wfile.write(body_json.encode("utf-8"))
            return
        else:
            print( "no" )
            print( self.path )
            return

    def do_DELETE(self):
        print( "delete" )
        local_path = self.path.strip("/").split("/")
        if( local_path[0] == "dat" ):
            print( "dat" )

            with open('./dat.json', 'w') as file_open:
                pass

            self.send_response(200)
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
            self.send_header('Access-Control-Allow-Headers', '*')
            self.end_headers()
            return
        else:
            print( "no" )
            print( self.path )
            return

def rest_server(port):
    httpd_rest = http.server.ThreadingHTTPServer(("", port), RestHandler)
    httpd_rest.serve_forever()


def main():
    #Serverstart
    port_rest  = 3333
    try:
        t1 = threading.Thread(target=rest_server,  args=(port_rest,),  daemon = True)

        t1.start()

        while True: time.sleep(1)

    except (KeyboardInterrupt, SystemExit):
        print("exit")
        sys.exit()

if __name__ == "__main__":
  main()

Der HTTP-Server wird an Port 3333 durch REST-Analyse mit do_OPTIONS / do_POST / do_GET / do_DELETE von Pythons BaseHTTPRequestHandler eingerichtet. Die Operation ist einfach, sie aktualisiert (POST), ruft den Inhalt ab (GET) und initialisiert (DELETE) die Datei "dat.json" im aktuellen Verzeichnis.

OPTIONEN? PUT?

REST akzeptiert normalerweise die folgenden vier Arten von Anforderungen.

Es sollte entsprechend erstellt werden, aber ich habe beschlossen, PUT nicht vorzubereiten, da POST als Ersatz verwendet werden kann.

Für "OPTIONEN", die nicht in den 4 Typen enthalten sind, wird abhängig von den Spezifikationen des Browsers vor der POST-Anforderung eine OPTIONS-Anforderung ausgegeben. Die im Header der Antwort auf diese OPTIONS-Anforderung enthaltenen "Access-Control-Allow-Methods" beschreiben die Methoden, die der Server verarbeiten kann. Wenn POST enthalten ist, wird die POST-Anforderung nach der OPTIONS-Anforderung ausgegeben. Ich werde. Dieser Vorgang wird als Preflight bezeichnet und ist ein Browser-Vorgang. Daher gibt es Problemumgehungen, z. B. das Erstellen einer Anforderung von der Serverseite aus. Wenn Sie jedoch Ihren eigenen Server vorbereiten, ist es einfacher, ihn der OPTIONS-Anforderung anzupassen, damit er kompatibel ist. Ich tat.

Bedienungsanleitung

Ich werde jeden Band erklären. do_OPTIONS

do_OPTIONS


    def do_OPTIONS(self):
        #Unterstützt Preflight-Anfragen
        print( "options" )
        self.send_response(200)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
        self.send_header('Access-Control-Allow-Headers', '*')
        self.end_headers()

Erstellen Sie eine Antwort auf die im vorherigen Abschnitt beschriebene OPTIONS-Anforderung. Es wird einfach eine Antwort zurückgegeben.

do_POST

do_POST


    def do_POST(self):
        print( "post" )
        local_path = self.path.strip("/").split("/")
        #Anfrage erhalten
        content_len  = int(self.headers.get("content-length"))
        body = json.loads(self.rfile.read(content_len).decode('utf-8'))

        #Antwortverarbeitung
        if( local_path[0] == "dat" ):
            if(os.path.getsize('./dat.json')):
              with open('./dat.json', 'r') as json_open:
                json_load = json.load(json_open)

              json_load.update(**body)
              json_wraite = json.dumps(json_load, sort_keys=False, indent=4, ensure_ascii=False)
            else:
              json_wraite = json.dumps(body, sort_keys=False, indent=4, ensure_ascii=False)

            with open('./dat.json', 'w') as json_open:
                json_open.write(json_wraite)

            self.send_response(200)
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
            self.send_header('Access-Control-Allow-Headers', '*')
            self.end_headers()
            return
        else:
            print( "no" )
            print( self.path )
            return

Der POST-Prozess schreibt die in der POST-Anforderung empfangenen JSON-Daten in eine Datei. Wir werden den Prozess in der Reihenfolge von oben verfolgen.

local_path = self.path.strip("/").split("/")

self.path enthält die URL-Daten der Anfrage. Teilen Sie dies mit "/" und behalten Sie es im Array. Wenn die Anforderung beispielsweise "Servername / aaa / bbb / ccc /" lautet, lautet der lokale Pfad wie folgt. local_path[0]='aaa' local_path[1]='bbb' local_path[2]='ccc'

content_len  = int(self.headers.get("content-length"))
body = json.loads(self.rfile.read(content_len).decode('utf-8'))

Es ist ein Prozess, die zusammen mit der Anforderung empfangenen JSON-Daten zu analysieren und in den Daten des Wörterbuchtyps zu speichern.

#Antwortverarbeitung
if( local_path[0] == "dat" ):
    if(os.path.getsize('./dat.json')):
      with open('./dat.json', 'r') as json_open:
        json_load = json.load(json_open)

      json_load.update(**body)
      json_wraite = json.dumps(json_load, sort_keys=False, indent=4, ensure_ascii=False)
    else:
      json_wraite = json.dumps(body, sort_keys=False, indent=4, ensure_ascii=False)

    with open('./dat.json', 'w') as json_open:
        json_open.write(json_wraite)

    self.send_response(200)
    self.send_header('Access-Control-Allow-Origin', '*')
    self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
    self.send_header('Access-Control-Allow-Headers', '*')
    self.end_headers()
    return

In der nächsten Phase überprüfen wir local_path [0] und definieren die API, wenn es sich um "dat" handelt. Im Beispiel wird nur local_path [0] verwendet, die API wird jedoch definiert, indem local_path [1] und local_path [2] nacheinander überprüft werden. Der Rest sind einfache Dateivorgänge. Der Grund für die Überprüfung der Dateigröße ist, dass beim Laden einer leeren Datei in json.load () ein Fehler auftritt. Wenn die Datei leer ist, werden die empfangenen Daten unverändert in die Datei geschrieben. (Die Dateivorgänge hier sind redundant und matschig.)

Der Teil, der die endgültige Antwort erstellt, ist derselbe wie OPTIONS, und die Fehlerbehandlung wird nicht berücksichtigt.

else:
    print( "no" )
    print( self.path )
    return

Dies ist der Zeitpunkt, an dem eine unerwartete URL eintrifft. Normalerweise sollte ein 404-Fehler zurückgegeben werden, aber da es sich nicht um einen öffentlichen Dienst handelt, wird er mutig weggelassen.

do_GET

do_GET


    def do_GET(self):
        print( "get" )
        local_path = self.path.strip("/").split("/")
        #Antwortverarbeitung
        if( local_path[0] == "dat" ):
            print( "dat" )
            if(os.path.getsize('./dat.json')):
              with open('./dat.json', 'r') as json_open:
                json_load = json.load(json_open)

            else:
              json_load = {}

            self.send_response(200)
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
            self.send_header('Access-Control-Allow-Headers', '*')
            self.send_header('Content-type', 'application/json;charset=utf-8')
            self.end_headers()
            body_json = json.dumps(json_load, sort_keys=False, indent=4, ensure_ascii=False) 
            self.wfile.write(body_json.encode("utf-8"))
            return
        else:
            print( "no" )
            print( self.path )
            return

Der grundlegende Teil ist der gleiche wie POST. Es eliminiert die Erfassung von JSON-Daten aus der Anforderung, liest die Datei und fügt diese Daten in die Antwort ein.

self.send_response(200)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
self.send_header('Access-Control-Allow-Headers', '*')
self.send_header('Content-type', 'application/json;charset=utf-8')
self.end_headers()
body_json = json.dumps(json_load, sort_keys=False, indent=4, ensure_ascii=False) 
self.wfile.write(body_json.encode("utf-8"))

Der Unterschied zum POST besteht darin, dass der Inhaltstyp zum Header hinzugefügt und Daten in den Körperteil geschrieben werden.

do_DELETE

do_DELETE


    def do_DELETE(self):
        print( "delete" )
        local_path = self.path.strip("/").split("/")
        if( local_path[0] == "dat" ):
            print( "dat" )

            with open('./dat.json', 'w') as file_open:
                pass

            self.send_response(200)
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
            self.send_header('Access-Control-Allow-Headers', '*')
            self.end_headers()
            return
        else:
            print( "no" )
            print( self.path )
            return

DELETE öffnet einfach die Zieldatei mit dem Attribut w und schließt sie, ohne etwas zu tun, um sie zu einer leeren Datei zu machen.

rest_server

rest_server


def rest_server(port):
    httpd_rest = http.server.ThreadingHTTPServer(("", port), RestHandler)
    httpd_rest.serve_forever()

Es ist eine Funktion, die den Server einfach an dem durch das Argument angegebenen Port startet.

main

main


def main():
    #Serverstart
    port_rest  = 3333
    try:
        t1 = threading.Thread(target=rest_server,  args=(port_rest,),  daemon = True)

        t1.start()

        while True: time.sleep(1)

    except (KeyboardInterrupt, SystemExit):
        print("exit")
        sys.exit()

Ich versuche, den Server in einem separaten Thread zu starten. Außerdem endet das Programm, wenn es einen End-Interrupt (Strg + C) von der Tastatur empfängt.

Funktionsprüfung

Wenn Sie das Skript starten, wird der Server an Port 3333 gestartet. Führen Sie daher den folgenden Befehl aus.

curl -X POST -H 'Content-Type:application/json' -d '{"key":"val"}' localhost:3333/dat
curl -X GET localhost:3333/dat
curl -X DELETE localhost:3333/dat

Der Befehl wird auf localhost gesetzt, da er auf demselben Computer wie der Server ausgeführt wird. Wenn Sie einen anderen Computer verwenden, versuchen Sie, die IP-Adresse des Computers zu verwenden, der der Server sein soll.

das Ende

Ich konnte vorerst einen REST-Server bauen. Wenn Sie eine API hinzufügen möchten, können Sie die Pfadanalyse immer mehr verschachteln. Da Sie problemlos einen anderen Befehl aufrufen können, können Sie ihn auch so erweitern, dass er auch für die Systemverwaltung verwendet werden kann.

Versuch es bitte.

Recommended Posts

Erstellen Sie einen (einfachen) REST-Server
Erstellen Sie einen einfachen Textlint-Server
Erstellen Sie mit GitHub Pages einen Pseudo-REST-API-Server
So erstellen Sie ein einfaches TCP-Server / Client-Skript
Erstellen Sie einen Unix Domain Socket-Server
Schreiben Sie einen supereinfachen TCP-Server
Erstellen Sie eine einfache GUI-App in Python
Erstellen Sie einen einfachen WebDAV-Server unter Linux
Erstellen Sie eine einfache Web-App mit Flasche
Richten Sie mit Python 3 einen einfachen HTTPS-Server ein
Richten Sie einen einfachen HTTPS-Server mit Asyncio ein
Erstellen wir eine REST-API mit SpringBoot + MongoDB
Starten Sie mit Docker einen einfachen Python-Webserver
Erstellen Sie in Python ein einfaches Momentum-Investmentmodell
Richten Sie einen einfachen SMTP-Server in Python ein
So erstellen Sie eine Rest-API in Django
Erstellen Sie in Tornado einen HTTP-Server (Hello World)
Erstellen Sie ein Python-Modul
Erstellen Sie eine bootfähige LV
Erstellen Sie eine Python-Umgebung
Erstellen Sie einen Slack Bot
Erstellen Sie eine Todo-App mit Django REST Framework + Angular
Richten Sie einen einfachen lokalen Server auf Ihrem Mac ein
Lassen Sie uns eine Todo-App mit dem Django REST-Framework erstellen
Erstellen Sie einen Home Music Server mit Centos8 + Universal Media Server
Erstellen Sie einen Webserver in der Sprache Go (net / http) (1)
Erstellen Sie mit Quarry einen gefälschten Minecraft-Server in Python
[Vagrant] Richten Sie einen einfachen API-Server mit Python ein
Erstellen Sie ein Wox-Plugin (Python)
Erstellen Sie eine Funktion in Python
Erstellen Sie ein Wörterbuch in Python
Ein einfaches Beispiel für pivot_table.
Django-Anfänger erstellen einfache Apps 3
Django-Anfänger erstellen einfache Apps 1
Starten Sie einen einfachen WEB-Server, der den Header überprüfen kann
Erstellen Sie eine Homepage mit Django
So geben Sie das öffentliche Verzeichnis Python Simple HTTP Server an
[Python Kivy] So erstellen Sie ein einfaches Popup-Fenster
Erstellen Sie ein Python-Numpy-Array
Erstellen Sie eine Dummy-Datendatei
Django-Anfänger erstellen einfache Apps 2
Erstellen Sie mit python wxpython + openCV ein einfaches Videoanalysetool
Erstellen Sie einen Django-Anmeldebildschirm
Erstellen Sie mit VSCode & Docker Desktop eine einfache Python-Entwicklungsumgebung
Erstellen Sie ein Klassenzimmer auf Jupyterhub
Einfacher HTTP-Server für Python
Erstellen Sie ein Verzeichnis mit Python
Erstellen Sie eine einfache CRUD-App mit der generischen Klassenansicht von Django
Django-Anfänger erstellen einfache Apps 5
Erstellen Sie einen rudimentären ELF-Packer
Ich habe versucht, eine Serverumgebung zu erstellen, die unter Windows 10 ausgeführt wird
[C-Sprache] [Linux] Versuchen Sie, einen einfachen Linux-Befehl zu erstellen. * Fügen Sie einfach hinzu! !!
Übersicht über das Erstellen eines Server-Sockets und das Einrichten eines Client-Sockets
Ich habe versucht, einen einfachen Kredit-Score mit logistischer Regression zu erstellen.
Rails-Benutzer versuchen, mit Django eine einfache Blog-Engine zu erstellen
Erstellen Sie einen einfachen geplanten Stapel mit Dockers Python Image und parse-crontab