Was ein Framework für dich tut
Die Bytes von FastAPI sehen
Jede Antwort, die deine App bisher gesendet hat, ging als Python-TestClient. Kein Byte davon hat einen
Ein Server, der anhält
Jeder Server in diesem Kurs ist begrenzt, und dieser auch. pl_http bindet einen Socket an 127.0.0.1 auf einem make_server es getan hat, übergibt diesen Socket an uvicorn und sagt uvicorn, dass es nach drei Anfragen anhalten soll. Dann sendet es drei Anfragen mit wire.fetch aus wire.py, dem Client aus Kapitel 9, und gibt die Bytes in beide Richtungen aus. uvicorn hält kurz nach der dritten Antwort von selbst an und schließt seinen lauschenden Socket. Wäre der Client früher fertig, würde pl_http uvicorn sagen, dass es anhalten soll, und höchstens 2 Sekunden darauf warten. Kein Server in diesem Kurs wartet darauf, dass du ihn von Hand anhältst.
Führe es im Tab Shell aus:
python pl_http.py uvicorn
Mit den GET /books. Wie in früheren Kapiteln nennt die Host-Zeile books.test, während die Verbindung zu 127.0.0.1 geht:
client -> server b'GET /books HTTP/1.1\r\nHost: books.test\r\n\r\n' (41 bytes)
server -> client b'HTTP/1.1 200 OK\r\ndate: ...\r\nserver: uvicorn\r\ncontent-length: 120\r\ncontent-type: application/json\r\n\r\n{"books":[{"id":1,"title":"Kindred","author":"Octavia E. Butler"},{"id":2,'... (246 bytes)
Die date-Zeile enthält die Uhrzeit deines Laufs, hier als ... gezeigt. pl_http kürzt eine lange Antwort, aber wire.fetch hat den ganzen Body gelesen:
body: b'{"books":[{"id":1,"title":"Kindred","author":"Octavia E. Butler"},{"id":2,"title":"Caf\xc3\xa9 Society","author":"Ana Ruiz"}]}' (120 bytes)
Auf ein : oder ein , folgt kein Leerzeichen. Das json.dumps deines Servers hat nach jedem ein Leerzeichen gesetzt, also ist sein Body für dieselben zwei Bücher 132 Bytes lang.
Die zweite Anfrage ist HEAD /books, und die 405 aus Lektion 2 kommt auf der Leitung an:
client -> server b'HEAD /books HTTP/1.1\r\nHost: books.test\r\n\r\n' (42 bytes)
server -> client b'HTTP/1.1 405 Method Not Allowed\r\ndate: ...\r\nserver: uvicorn\r\nallow: GET\r\ncontent-length: 31\r\ncontent-type: application/json\r\n\r\n' (153 bytes)
Die content-length von 31 ist die Größe des Bodys, den FastAPI für diesen Fehler hat, {"detail":"Method Not Allowed"}, und es folgt kein Body: uvicorn sendet keinen als Antwort auf HEAD, die Regel, die du in Lektion 4.5 von Hand geschrieben hast. Die dritte Anfrage fragt nach einem Buch, das es nicht gibt:
client -> server b'GET /books/99 HTTP/1.1\r\nHost: books.test\r\n\r\n' (44 bytes)
server -> client b'HTTP/1.1 404 Not Found\r\ndate: ...\r\nserver: uvicorn\r\ncontent-length: 25\r\ncontent-type: application/json\r\n\r\n{"detail":"no such book"}' (157 bytes)
wire.fetch brauchte für nichts davon etwas Neues. Es hat jeden Kopf gelesen, content-length gefunden und genau so viele Bytes gelesen, oder keine nach HEAD: die Framing-Regel aus Kapitel 9. Keine Antwort sagt connection: close, also hält uvicorn unter wire.fetch schließt seine Seite, sobald es ihn gelesen hat. Ein Client braucht nur Bytes, die HTTP folgen, egal welches Programm sie geschrieben hat. Der Lauf endet so:
--- uvicorn's log ---
(nothing)
--- result ---
uvicorn stopped on its own after 3 requests; the listening socket is closed
pl_http: every thread has stopped
Wenn eine Funktion noch NotImplementedError auslöst, bekommt ihre Anfrage HTTP/1.1 500 Internal Server Error, und das Log von uvicorn nennt die error: Exception in ASGI application: NotImplementedError: not written yet: Lesson 1.
Wer welche Zeile geschrieben hat
Jede Zeile der 404 stammt von einer von drei Stellen:
| Zeile | Geschrieben von |
|---|---|
der Status 404 | deiner Funktion, über HTTPException |
Not Found und der Rest der Statuszeile | uvicorn |
date und server: uvicorn | uvicorn |
content-type: application/json und content-length: 25 | FastAPI, das die Antwort als |
der Body {"detail":"no such book"} | deine Funktion hat den Grund gewählt; FastAPI hat das JSON geschrieben |
Bei der 405 hat FastAPI den Status gewählt und auch allow: GET geschrieben; deine Funktion lief nie. Die Header-Namen sind kleingeschrieben, weil die Schnittstelle zwischen App und Server, ASGI, sie so übergibt. Deshalb schreibt Starlette, das Toolkit, auf dem FastAPI aufbaut, content-type, content-length und allow klein, und uvicorn schreibt seine eigenen date- und server-Zeilen genauso. http.server hat Server und Date geschrieben. Namen ignorieren Groß- und Kleinschreibung, also ist beides korrekt.
Noch etwas passiert außer Sichtweite. FastAPI führt eine einfache def-Funktion wie get_book in einem Worker-Thread aus, damit eine langsame Funktion den Server nicht aufhält, während sie wartet. Eine Funktion, die mit async def geschrieben ist, würde stattdessen einen Thread viele Wartezeiten überlappen lassen; dieser Kurs bleibt bei einfachem def, und Kapitel 13 nennt asynchrones HTTP als einen Weg, dem du folgen kannst.
Welche Zeile hat uvicorn hinzugefügt, nicht deine Funktion und nicht FastAPI?
Dein Server hat für dieselben zwei Bücher 132 Body-Bytes gesendet; dieser Lauf zeigt 120. Warum?
Warum endet dieser Lauf von selbst?
Dieser Lauf ist zum Zuschauen da, und nichts darin wird bewertet. Lektion 5 macht die App mit DELETE und der Query limit fertig. Danach holt Kapitel 12 deinen eigenen Server zurück und verbindet ihn mit einem Client.