Kapitel "Anforderungs- / Antwort- / Ansichtsklasse erstellen, um die Sichtbarkeit zu verbessern" Das wurde aktualisiert.
Wenn Sie mehr lesen möchten, "mögen" oder "folgen Sie mir" in Buch ;-)
Das Folgende ist ein Auszug aus dem Inhalt des Buches.
Es gibt jetzt drei Endpunkte, die dynamische Antworten generieren, und workerthread.py
liegt jetzt nahe bei 200 Zeilen.
Selbst zu diesem Zeitpunkt mache ich viele verschiedene Dinge mit einer Datei, sodass selbst 200 Zeilen zu einem überfüllten Modul mit sehr schlechter Sicht geworden sind.
Wenn Sie diese Webanwendung weiterentwickeln, werden Sie außerdem immer mehr Endpunkte haben.
Es ist offensichtlich, dass eine Wartung nicht möglich ist, wenn Sie sie jedes Mal zu "workerthread.py" hinzufügen.
Man kann sagen, dass es notwendig wurde, die Sichtbarkeit von workerthread.py
durch Trennen von Verantwortlichkeiten und Teilen von Dateien zu verbessern.
Mit anderen Worten, es ist an der Zeit, dass die Saison für das Refactoring gekommen ist.
In diesem Kapitel werden wir "Verarbeitung, die dynamisch einen Antworttext für jeden Endpunkt generiert" für ein externes Modul ausschneiden.
Lassen Sie uns zunächst einfach den HTML-Generierungsprozess für jeden Endpunkt in ein anderes Modul ausschneiden.
Der Name des auszuschneidenden Moduls lautet "Ansichten". Dies liegt daran, dass es sich um ein Modul handelt, dessen Aufgabe es ist, unabhängig von der HTTP-Situation, wie z. B. der Verbindung oder dem Parsen des Headers, nur den Ansichtsteil (= Anforderungshauptteil) zu generieren.
study/workerthread.py
https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16/workerthread.py#L50-L59
study/views.py
https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16/views.py
study/workerthread.py
if path == "/now":
response_body, content_type, response_line = views.now()
elif path == "/show_request":
response_body, content_type, response_line = views.show_request(
method, path, http_version, request_header, request_body
)
elif path == "/parameters":
response_body, content_type, response_line = views.parameters(method, request_body)
Bis zum letzten Mal schrieb ich, dass der Prozess des Generierens von HTML für jeden Pfad schwierig war, aber zuerst entschied ich mich, diesen Teil auf die Funktion des Moduls "Ansichten" auszuschneiden.
dadurch,
--workerthread.py
empfängt die HTTP-Anforderung, analysiert sie, ruft den Antwortinhalt von der Funktion des views
-Moduls gemäß dem Pfad ab, erstellt die HTTP-Antwort und gibt sie an den Client zurück.
--views.py
hat eine Funktion für jeden Pfad, empfängt den Inhalt der Anforderung und gibt den Inhalt der dynamisch generierten Antwort zurück.
Die Aufgabe, "den Inhalt der Antwort dynamisch gemäß dem Pfad zu generieren", wurde in "Ansichten" herausgeschnitten.
study/views.py
import textwrap
import urllib.parse
from datetime import datetime
from pprint import pformat
from typing import Tuple, Optional
def now() -> Tuple[bytes, Optional[str], str]:
"""
Generieren Sie HTML, um die aktuelle Uhrzeit anzuzeigen
"""
html = f"""\
<html>
<body>
<h1>Now: {datetime.now()}</h1>
</body>
</html>
"""
response_body = textwrap.dedent(html).encode()
# Content-Geben Sie den Typ an
content_type = "text/html; charset=UTF-8"
#Generieren Sie eine Antwortzeile
response_line = "HTTP/1.1 200 OK\r\n"
return response_body, content_type, response_line
def show_request(
method: str,
path: str,
http_version: str,
request_header: dict,
request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:
"""
Generieren Sie HTML, um den Inhalt der HTTP-Anforderung anzuzeigen
"""
html = f"""\
<html>
<body>
<h1>Request Line:</h1>
<p>
{method} {path} {http_version}
</p>
<h1>Headers:</h1>
<pre>{pformat(request_header)}</pre>
<h1>Body:</h1>
<pre>{request_body.decode("utf-8", "ignore")}</pre>
</body>
</html>
"""
response_body = textwrap.dedent(html).encode()
# Content-Geben Sie den Typ an
content_type = "text/html; charset=UTF-8"
#Generieren Sie eine Antwortzeile
response_line = "HTTP/1.1 200 OK\r\n"
return response_body, content_type, response_line
def parameters(
method: str,
request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:
"""
HTML anzeigen mit POST-Parametern
"""
#Gibt 405 für GET-Anforderungen zurück
if method == "GET":
response_body = b"<html><body><h1>405 Method Not Allowed</h1></body></html>"
content_type = "text/html; charset=UTF-8"
response_line = "HTTP/1.1 405 Method Not Allowed\r\n"
elif method == "POST":
post_params = urllib.parse.parse_qs(request_body.decode())
html = f"""\
<html>
<body>
<h1>Parameters:</h1>
<pre>{pformat(post_params)}</pre>
</body>
</html>
"""
response_body = textwrap.dedent(html).encode()
# Content-Geben Sie den Typ an
content_type = "text/html; charset=UTF-8"
#Generieren Sie eine Antwortzeile
response_line = "HTTP/1.1 200 OK\r\n"
return response_body, content_type, response_line
Dies ist auch nicht besonders schwierig. Ich habe gerade den Prozess der dynamischen Generierung der Antwort eingeführt, die ursprünglich in "workerthread.py" geschrieben wurde.
Es ist gut, die Ansichtsfunktion auszuschneiden, aber wie es jetzt ist, ist die Anzahl der Argumente für jede Funktion unterschiedlich. "Das Pseudonym, das diesen Pfad verarbeitet, benötigt die Argumente von diesem und jenem, und die Funktion, die diesen Pfad verarbeitet, benötigt die Argumente von diesem und jenem und jenem ..." Und so weiter muss der Anrufer die Details des Anrufers kennen.
In der Welt der Programmierung ist bekannt, dass der Quellcode einfach wird, wenn ein Modul erstellt wird, so dass die Details des anderen Moduls nicht so weit wie möglich bekannt sind.
Lassen Sie uns im nächsten SCHRITT etwas mehr mit dem Refactoring fortfahren und dies erkennen.
Jetzt muss die WorkerThread-Klasse die Details der Ansichtsfunktion kennen, da sie nicht aufgerufen werden kann, ohne zu wissen, welche und wie viele Argumente sie für jede Funktion benötigt.
Eine einfache Möglichkeit, diese Situation zu beseitigen, ist ** "Ich weiß nicht, welchen Parameter jede Funktion verwendet, aber ich werde trotzdem alles übergeben" **.
Schauen wir uns den Quellcode an.
study/workerthread.py
https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16-2/workerthread.py
study/views.py
https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16-2/views.py
study/views.py
Beginnen wir mit views.py
def now(
method: str,
path: str,
http_version: str,
request_header: dict,
request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:
def parameters(
method: str,
path: str,
http_version: str,
request_header: dict,
request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:
Die Argumente sind in allen Ansichtsfunktionen vereinheitlicht, sodass alle Anforderungsinformationen empfangen werden können. Diese Argumente werden in der Funktion nicht verwendet, aber wenn der Aufrufer sie zur Verfügung stellt, muss er nicht darüber nachdenken, "was benötigt wird und was nicht".
study/workerthread.py
Als nächstes kommt die Seite, die die Ansichtsfunktion aufruft.
#Korrespondenz zwischen Pfad- und Ansichtsfunktionen
URL_VIEW = {
"/now": views.now,
"/show_request": views.show_request,
"/parameters": views.parameters,
}
Die Entsprechung zwischen den Pfad- und Ansichtsfunktionen wird als Konstante definiert. Es ist ein ** Wörterbuch mit Pfad als Schlüssel und ** Ansichtsfunktion, die Pfad als Wert entspricht.
Abhängig von der Sprache können Sie überrascht sein, "eine" Funktion "als Wörterbuchwert (oder assoziatives Array) festzulegen" oder "einer Variablen eine" Funktion "zuzuweisen", wie oben beschrieben.
Aber in Python ist dies eine legitime Behandlung.
Objekte, die als Werte behandelt werden können, z. B. die Zuweisung zu Variablen und die Übergabe an Operationen und Funktionen (als Argumente und Rückgabewerte), werden als ** erstklassige Objekte ** bezeichnet. In Python ** sind alle Objekte primäre Objekte ** und Funktionen sind keine Ausnahme.
Daher ist es möglich, einer Variablen eine Funktion zuzuweisen oder eine Funktion zu erstellen, die eine Funktion empfängt und eine Funktion zurückgibt.
Letzteres ist als "Metaprogrammierung" bekannt und jeder Interessierte sollte es ausprobieren.
#Wenn es eine Ansichtsfunktion gibt, die dem Pfad entspricht, rufen Sie die Funktion ab und rufen Sie sie auf, um eine Antwort zu generieren
if path in self.URL_VIEW:
view = self.URL_VIEW[path]
response_body, content_type, response_line = view(
method, path, http_version, request_header, request_body
)
path in self.URL_VIEW
prüft, ob der Schlüssel im Wörterbuch self.URL_VIEW`` path
enthält.
Mit anderen Worten, wir prüfen, ob die dem Pfad entsprechende Ansichtsfunktion registriert ist.
Wenn es registriert wurde, wird der Wert des Wörterbuchs, das diesem Schlüssel entspricht, erfasst und der Variablen "Ansicht" zugewiesen.
Das heißt, der Variablen view
wird die ** Ansichtsfunktion ** zugewiesen (anstelle des Rückgabewerts beim Aufrufen der Ansichtsfunktion).
In der letzten Zeile wird view (~~)
verwendet, um die der Variablen view
zugewiesene Funktion aufzurufen und den Rückgabewert abzurufen.
Es ist erwähnenswert, dass ** alle Ansichtsfunktionen jetzt dieselben Argumente akzeptieren (method, path, http_version, request_header, request_body
), wodurch die Ansichtsfunktion abstrahiert wird. ** ** **
Bisher waren die Argumente für jede Funktion unterschiedlich. Selbst wenn Sie "Aufruf der Ansichtsfunktion" sagten, konnten Sie sie nur dann korrekt aufrufen, wenn Sie wussten, "welche Art von Funktion die Funktion ist". Durch Vereinheitlichen der Argumente (= Vereinheitlichen der Schnittstelle) ** "Ich weiß nicht, was die Funktion ist, aber ich kann sie trotzdem aufrufen" **.
Dadurch entfällt die Notwendigkeit einer Verzweigung nach Pfad (oder Funktion) innerhalb von "Workerthread".
Auf diese Weise wird "es ist nicht notwendig, mit konkreten Dingen umzugehen, indem nur ein Teil der gemeinsamen Eigenschaften aus konkreten Dingen extrahiert wird" als "Abstraktion" bezeichnet. Es ist eine sehr wichtige Technik in der Programmierung.
In diesem Fall durch Vereinheitlichen der Schnittstelle von konkreten Funktionen wie now ()
show_rewuest ()
parameters
"Nimmt fünf Argumente" Methode, Pfad, http_version, request_header, request_body "und gibt zwei Werte" response_body, response_line "zurück."
Durch Extrahieren (= Abstrahieren) nur der Eigenschaft, des Aufrufers
"Ich weiß nicht, wie viele Funktionen es sind, aber ich nenne es mit 5 Argumenten."
Dies bedeutet, dass es so gehandhabt werden kann.
Oder Sie könnten "einheitliche Schnittstelle für die Abstraktion" sagen.
Es ist gut, dass die Ansichtsfunktion eine gemeinsame Oberfläche hat und der Aufrufer eine bessere Ansicht hat, aber es gibt viele fünf Argumente.
Die Tatsache, dass eine HTTP-Anfrage viele Informationen enthält, ist unvermeidlich, aber es ist umständlich, sie in separaten Variablen zu verteilen und zu speichern.
Erstellen wir also eine Klasse, die die HTTP-Anforderung ausdrückt, und fügen die Informationen dort zusammen.
Dies vereinfacht auch die Oberfläche der Ansichtsfunktion.
Kapitel "Anforderungs- / Antwort- / Ansichtsklasse erstellen, um die Sichtbarkeit zu verbessern"
Recommended Posts