0%

Was ein Framework für dich tut

Die Bytes von FastAPI sehen

Jede Antwort, die deine App bisher gesendet hat, ging als Python- von zu TestClient. Kein Byte davon hat einen durchquert, also hast du noch nicht gesehen, wie die 404 von FastAPI auf der Leitung aussieht. Diese Arbeit erledigt ein Serverprogramm: Es nimmt Verbindungen an, liest die Bytes der Anfragen, übergibt jede Anfrage an die App und schreibt die Antwort der App als Bytes. Für FastAPI ist dieses Programm oft uvicorn, und das Hilfsprogramm dieses Kapitels kann es für dich ausführen.

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 , den das System wählt, so wie dein 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 aus Lektion 1 an ihrem Platz ist der erste Austausch 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 /1.1 jede Verbindung für eine weitere Anfrage offen, und die Länge ist der einzige Weg, auf dem der Client erfährt, wo ein Body endet; 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 , etwa 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:

ZeileGeschrieben von
der Status 404deiner Funktion, über HTTPException
Not Found und der Rest der Statuszeileuvicorn
date und server: uvicornuvicorn
content-type: application/json und content-length: 25FastAPI, das die Antwort als codiert und ihre Bytes gezählt hat
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.