PEP 333 – Python Web Server Gateway Interface v1.0
- Autor:
- Phillip J. Eby <pje at telecommunity.com>
- Discussions-To:
- Web-SIG Liste
- Status:
- Final
- Typ:
- Informational
- Erstellt:
- 07-Dez-2003
- Post-History:
- 07-Dez-2003, 08-Aug-2004, 20-Aug-2004, 27-Aug-2004, 27-Sep-2010
- Ersetzt-Durch:
- 3333
Vorwort
Hinweis: Für eine aktualisierte Version dieser Spezifikation, die Python 3.x unterstützt und Community-Fehlerkorrekturen, Nachträge und Klarstellungen enthält, siehe stattdessen PEP 3333.
Zusammenfassung
Dieses Dokument spezifiziert eine vorgeschlagene Standard-Schnittstelle zwischen Webservern und Python-Webanwendungen oder -Frameworks, um die Portabilität von Webanwendungen über eine Vielzahl von Webservern hinweg zu fördern.
Begründung und Ziele
Python bietet derzeit eine breite Palette von Webanwendungs-Frameworks wie Zope, Quixote, Webware, SkunkWeb, PSO und Twisted Web – um nur einige zu nennen [1]. Diese große Auswahl kann für neue Python-Benutzer ein Problem darstellen, da im Allgemeinen ihre Wahl des Web-Frameworks ihre Wahl nutzbarer Webserver einschränkt und umgekehrt.
Im Gegensatz dazu ist es Java, obwohl es genauso viele Webanwendungs-Frameworks gibt, dank der „Servlet“-API von Java möglich, Anwendungen, die mit jedem Java-Webanwendungs-Framework geschrieben wurden, auf jedem Webserver auszuführen, der die Servlet-API unterstützt.
Die Verfügbarkeit und weit verbreitete Nutzung einer solchen API in Webservern für Python – ob diese Server in Python geschrieben sind (z. B. Medusa), Python einbetten (z. B. mod_python) oder Python über ein Gateway-Protokoll aufrufen (z. B. CGI, FastCGI usw.) – würde die Wahl des Frameworks von der Wahl des Webservers trennen und Benutzern die Freiheit geben, eine Kombination zu wählen, die ihnen passt, während sie Framework- und Serverentwicklern die Konzentration auf ihren bevorzugten Spezialbereich ermöglicht.
Dieser PEP schlägt daher eine einfache und universelle Schnittstelle zwischen Webservern und Webanwendungen oder Frameworks vor: die Python Web Server Gateway Interface (WSGI).
Aber die bloße Existenz einer WSGI-Spezifikation tut nichts, um den bestehenden Zustand von Servern und Frameworks für Python-Webanwendungen zu verbessern. Server- und Framework-Autoren und -Betreuer müssen WSGI tatsächlich implementieren, damit es irgendeine Wirkung hat.
Da jedoch keine bestehenden Server oder Frameworks WSGI unterstützen, gibt es für einen Autor, der die WSGI-Unterstützung implementiert, wenig sofortigen Lohn. Daher **muss** WSGI einfach zu implementieren sein, damit die anfängliche Investition eines Autors in die Schnittstelle angemessen gering sein kann.
Daher sind die Einfachheit der Implementierung **sowohl** auf der Server- als auch auf der Framework-Seite der Schnittstelle absolut entscheidend für den Nutzen der WSGI-Schnittstelle und sind daher das Hauptkriterium für alle Designentscheidungen.
Beachten Sie jedoch, dass die Einfachheit der Implementierung für einen Framework-Autor nicht dasselbe ist wie die Benutzerfreundlichkeit für einen Webanwendungsautor. WSGI bietet dem Framework-Autor eine absolut „nüchterne“ Schnittstelle, da Schnickschnack wie Response-Objekte und Cookie-Handling einfach im Weg der bestehenden Framework-Behandlung dieser Themen stehen würde. Wiederum ist das Ziel von WSGI, die einfache Verbindung bestehender Server und Anwendungen oder Frameworks zu erleichtern, nicht, ein neues Web-Framework zu schaffen.
Beachten Sie auch, dass dieses Ziel WSGI davon ausschließt, etwas zu verlangen, das nicht bereits in eingesetzten Python-Versionen verfügbar ist. Daher werden keine neuen Standardbibliotheksmodule von dieser Spezifikation vorgeschlagen oder gefordert, und nichts in WSGI erfordert eine Python-Version größer als 2.2.2. (Es wäre jedoch eine gute Idee, wenn zukünftige Python-Versionen die Unterstützung für diese Schnittstelle in den von der Standardbibliothek bereitgestellten Webservern enthalten würden.)
Zusätzlich zur einfachen Implementierung für bestehende und zukünftige Frameworks und Server sollte es auch einfach sein, Anfrage-Vorverarbeiter, Antwort-Nachbearbeiter und andere WSGI-basierte „Middleware“-Komponenten zu erstellen, die für ihren enthaltenden Server wie eine Anwendung aussehen, während sie für ihre enthaltenden Anwendungen als Server fungieren.
Wenn Middleware sowohl einfach als auch robust sein kann und WSGI in Servern und Frameworks weit verbreitet ist, ermöglicht dies die Möglichkeit einer völlig neuen Art von Python-Webanwendungs-Frameworks: eines, das aus lose gekoppelten WSGI-Middleware-Komponenten besteht. Tatsächlich können bestehende Framework-Autoren ihre Framework-Dienste sogar so umstrukturieren, dass sie auf diese Weise bereitgestellt werden, und eher wie Bibliotheken werden, die mit WSGI verwendet werden, und weniger wie monolithische Frameworks. Dies würde es dann Anwendungentwicklern ermöglichen, „Best-of-Breed“-Komponenten für bestimmte Funktionalitäten auszuwählen, anstatt sich auf alle Vor- und Nachteile eines einzelnen Frameworks festlegen zu müssen.
Natürlich ist dieser Tag zum Zeitpunkt der Erstellung dieses Dokuments zweifellos noch weit entfernt. In der Zwischenzeit ist es ein ausreichendes kurzfristiges Ziel für WSGI, die Verwendung jedes Frameworks mit jedem Server zu ermöglichen.
Schließlich sei erwähnt, dass die aktuelle Version von WSGI keinen bestimmten Mechanismus für die „Bereitstellung“ einer Anwendung zur Verwendung mit einem Webserver oder Server-Gateway vorschreibt. Derzeit ist dies notwendigerweise implementierungsabhängig durch den Server oder das Gateway. Nachdem eine ausreichende Anzahl von Servern und Frameworks WSGI implementiert hat, um Praxiserfahrung mit unterschiedlichen Bereitstellungsanforderungen zu sammeln, kann es sinnvoll sein, einen weiteren PEP zu erstellen, der einen Bereitstellungsstandard für WSGI-Server und Anwendungs-Frameworks beschreibt.
Übersicht der Spezifikation
Die WSGI-Schnittstelle hat zwei Seiten: die „Server“- oder „Gateway“-Seite und die „Anwendungs“- oder „Framework“-Seite. Die Serverseite ruft ein aufrufbares Objekt auf, das von der Anwendungsseite bereitgestellt wird. Die Details, wie dieses Objekt bereitgestellt wird, sind dem Server oder Gateway überlassen. Es wird davon ausgegangen, dass einige Server oder Gateways vom Bereitsteller einer Anwendung verlangen, ein kurzes Skript zu schreiben, um eine Instanz des Servers oder Gateways zu erstellen und es mit dem Anwendungsobjekt zu versorgen. Andere Server und Gateways können Konfigurationsdateien oder andere Mechanismen verwenden, um anzugeben, wo ein Anwendungsobjekt importiert oder anderweitig bezogen werden soll.
Zusätzlich zu „reinen“ Servern/Gateways und Anwendungen/Frameworks ist es auch möglich, „Middleware“-Komponenten zu erstellen, die beide Seiten dieser Spezifikation implementieren. Solche Komponenten fungieren als Anwendung für ihren enthaltenden Server und als Server für eine enthaltene Anwendung und können verwendet werden, um erweiterte APIs, Inhalts-Transformationen, Navigation und andere nützliche Funktionen bereitzustellen.
In dieser Spezifikation verwenden wir den Begriff „ein aufrufbares Objekt“ (a callable), um „eine Funktion, Methode, Klasse oder eine Instanz mit einer __call__-Methode“ zu bedeuten. Es obliegt dem Server, Gateway oder der Anwendung, die das aufrufbare Objekt implementiert, die geeignete Implementierungstechnik für ihre Bedürfnisse zu wählen. Umgekehrt darf ein Server, Gateway oder eine Anwendung, die ein aufrufbares Objekt aufruft, keine Abhängigkeit davon haben, welche Art von aufrufbarem Objekt bereitgestellt wurde. Aufrufbare Objekte dürfen nur aufgerufen, nicht aber introspektiert werden.
Die Anwendungs-/Framework-Seite
Das Anwendungsobjekt ist einfach ein aufrufbares Objekt, das zwei Argumente akzeptiert. Der Begriff „Objekt“ sollte nicht als Anforderung einer tatsächlichen Objektinstanz missverstanden werden: Eine Funktion, Methode, Klasse oder eine Instanz mit einer __call__-Methode sind alle akzeptabel für die Verwendung als Anwendungsobjekt. Anwendungsobjekte müssen mehrfach aufgerufen werden können, da praktisch alle Server/Gateways (außer CGI) solche wiederholten Anfragen stellen werden.
(Hinweis: Obwohl wir uns auf ein „Anwendungs“-Objekt beziehen, sollte dies nicht so interpretiert werden, dass Anwendungsentwickler WSGI als Web-Programmier-API verwenden! Es wird davon ausgegangen, dass Anwendungsentwickler weiterhin bestehende, hochrangige Framework-Dienste zur Entwicklung ihrer Anwendungen nutzen werden. WSGI ist ein Werkzeug für Framework- und Serverentwickler und ist nicht dazu gedacht, Anwendungsentwickler direkt zu unterstützen.)
Hier sind zwei Beispiel-Anwendungsobjekte; eines ist eine Funktion und das andere eine Klasse
def simple_app(environ, start_response):
"""Simplest possible application object"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return ['Hello world!\n']
class AppClass:
"""Produce the same output, but using a class
(Note: 'AppClass' is the "application" here, so calling it
returns an instance of 'AppClass', which is then the iterable
return value of the "application callable" as required by
the spec.
If we wanted to use *instances* of 'AppClass' as application
objects instead, we would have to implement a '__call__'
method, which would be invoked to execute the application,
and we would need to create an instance for use by the
server or gateway.
"""
def __init__(self, environ, start_response):
self.environ = environ
self.start = start_response
def __iter__(self):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
self.start(status, response_headers)
yield "Hello world!\n"
Die Server-/Gateway-Seite
Der Server oder das Gateway ruft das Anwendungs-Callable einmal pro empfangener Anfrage von einem HTTP-Client auf, die an die Anwendung gerichtet ist. Zur Veranschaulichung ist hier ein einfaches CGI-Gateway, implementiert als Funktion, die ein Anwendungsobjekt entgegennimmt. Beachten Sie, dass dieses einfache Beispiel eine eingeschränkte Fehlerbehandlung aufweist, da eine nicht abgefangene Ausnahme standardmäßig nach sys.stderr ausgegeben und vom Webserver protokolliert wird.
import os, sys
def run_with_cgi(application):
environ = dict(os.environ.items())
environ['wsgi.input'] = sys.stdin
environ['wsgi.errors'] = sys.stderr
environ['wsgi.version'] = (1, 0)
environ['wsgi.multithread'] = False
environ['wsgi.multiprocess'] = True
environ['wsgi.run_once'] = True
if environ.get('HTTPS', 'off') in ('on', '1'):
environ['wsgi.url_scheme'] = 'https'
else:
environ['wsgi.url_scheme'] = 'http'
headers_set = []
headers_sent = []
def write(data):
if not headers_set:
raise AssertionError("write() before start_response()")
elif not headers_sent:
# Before the first output, send the stored headers
status, response_headers = headers_sent[:] = headers_set
sys.stdout.write('Status: %s\r\n' % status)
for header in response_headers:
sys.stdout.write('%s: %s\r\n' % header)
sys.stdout.write('\r\n')
sys.stdout.write(data)
sys.stdout.flush()
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
if headers_sent:
# Re-raise original exception if headers sent
raise exc_info[0], exc_info[1], exc_info[2]
finally:
exc_info = None # avoid dangling circular ref
elif headers_set:
raise AssertionError("Headers already set!")
headers_set[:] = [status, response_headers]
return write
result = application(environ, start_response)
try:
for data in result:
if data: # don't send headers until body appears
write(data)
if not headers_sent:
write('') # send headers now if body was empty
finally:
if hasattr(result, 'close'):
result.close()
Middleware: Komponenten, die beide Seiten bespielen
Beachten Sie, dass ein einzelnes Objekt in Bezug auf einige Anwendungen die Rolle eines Servers spielen kann, während es gleichzeitig als Anwendung in Bezug auf einige Server fungiert. Solche „Middleware“-Komponenten können Funktionen wie folgt bereitstellen:
- Weiterleitung einer Anfrage an verschiedene Anwendungsobjekte basierend auf der Ziel-URL, nachdem die
environentsprechend umgeschrieben wurde. - Ermöglichen, dass mehrere Anwendungen oder Frameworks nebeneinander im selben Prozess laufen
- Lastverteilung und Remote-Verarbeitung durch Weiterleitung von Anfragen und Antworten über ein Netzwerk
- Ausführung von Inhaltsnachbearbeitung, wie z. B. Anwendung von XSL-Stylesheets
Die Anwesenheit von Middleware im Allgemeinen ist für die „Server/Gateway“- und die „Anwendungs/Framework“-Seiten der Schnittstelle transparent und erfordert keine spezielle Unterstützung. Ein Benutzer, der Middleware in eine Anwendung integrieren möchte, stellt die Middleware-Komponente einfach dem Server zur Verfügung, als wäre sie eine Anwendung, und konfiguriert die Middleware-Komponente so, dass sie die Anwendung aufruft, als wäre die Middleware-Komponente ein Server. Natürlich kann die „Anwendung“, die die Middleware umschließt, tatsächlich eine weitere Middleware-Komponente sein, die eine weitere Anwendung umschließt, und so weiter, wodurch ein sogenannter „Middleware-Stack“ entsteht.
Größtenteils muss Middleware den Einschränkungen und Anforderungen beider Seiten, Server und Anwendung, von WSGI entsprechen. In einigen Fällen sind die Anforderungen für Middleware jedoch strenger als für einen „reinen“ Server oder eine Anwendung, und diese Punkte werden in der Spezifikation vermerkt.
Hier ist ein (tongue-in-cheek) Beispiel einer Middleware-Komponente, die text/plain-Antworten in Pig Latin umwandelt, unter Verwendung von Joe Strouts piglatin.py. (Hinweis: Eine „echte“ Middleware-Komponente würde wahrscheinlich eine robustere Methode zur Überprüfung des Inhaltstyps verwenden und sollte auch die Codierung des Inhalts prüfen. Auch dieses einfache Beispiel ignoriert die Möglichkeit, dass ein Wort über eine Blockgrenze aufgeteilt sein könnte.)
from piglatin import piglatin
class LatinIter:
"""Transform iterated output to piglatin, if it's okay to do so
Note that the "okayness" can change until the application yields
its first non-empty string, so 'transform_ok' has to be a mutable
truth value.
"""
def __init__(self, result, transform_ok):
if hasattr(result, 'close'):
self.close = result.close
self._next = iter(result).next
self.transform_ok = transform_ok
def __iter__(self):
return self
def next(self):
if self.transform_ok:
return piglatin(self._next())
else:
return self._next()
class Latinator:
# by default, don't transform output
transform = False
def __init__(self, application):
self.application = application
def __call__(self, environ, start_response):
transform_ok = []
def start_latin(status, response_headers, exc_info=None):
# Reset ok flag, in case this is a repeat call
del transform_ok[:]
for name, value in response_headers:
if name.lower() == 'content-type' and value == 'text/plain':
transform_ok.append(True)
# Strip content-length if present, else it'll be wrong
response_headers = [(name, value)
for name, value in response_headers
if name.lower() != 'content-length'
]
break
write = start_response(status, response_headers, exc_info)
if transform_ok:
def write_latin(data):
write(piglatin(data))
return write_latin
else:
return write
return LatinIter(self.application(environ, start_latin), transform_ok)
# Run foo_app under a Latinator's control, using the example CGI gateway
from foo_app import foo_app
run_with_cgi(Latinator(foo_app))
Details der Spezifikation
Das Anwendungsobjekt muss zwei Positionsargumente akzeptieren. Zur Veranschaulichung haben wir sie environ und start_response genannt, aber sie müssen nicht diese Namen haben. Ein Server oder Gateway **muss** das Anwendungsobjekt mit Positions- (nicht Schlüsselwort-) Argumenten aufrufen. (D. h. durch Aufruf von result = application(environ, start_response), wie oben gezeigt.)
Der environ-Parameter ist ein Dictionary-Objekt, das Umgebungsvariablen im CGI-Stil enthält. Dieses Objekt **muss** ein eingebautes Python-Dictionary sein (nicht eine Unterklasse, UserDict oder eine andere Dictionary-Emulation), und die Anwendung darf das Dictionary nach Belieben modifizieren. Das Dictionary muss auch bestimmte WSGI-erforderliche Variablen enthalten (die in einem späteren Abschnitt beschrieben werden) und kann auch serverspezifische Erweiterungsvariablen enthalten, die nach einer unten beschriebenen Konvention benannt sind.
Der start_response-Parameter ist ein aufrufbares Objekt, das zwei erforderliche Positionsargumente und ein optionales Argument akzeptiert. Zur Veranschaulichung haben wir diese Argumente status, response_headers und exc_info genannt, aber sie müssen nicht diese Namen haben, und die Anwendung **muss** den start_response-Callable mit Positionsargumenten aufrufen (z. B. start_response(status, response_headers)).
Der status-Parameter ist ein Status-String der Form "999 Nachricht hier", und response_headers ist eine Liste von Tupeln (header_name, header_value), die den HTTP-Antwortheader beschreiben. Der optionale exc_info-Parameter wird unten in den Abschnitten The start_response() Callable und Error Handling beschrieben. Er wird nur verwendet, wenn die Anwendung einen Fehler abgefangen hat und versucht, eine Fehlermeldung an den Browser zu senden.
Der start_response-Callable muss einen write(body_data)-Callable zurückgeben, der einen Positions-Parameter akzeptiert: einen String, der als Teil des HTTP-Antwortkörpers geschrieben werden soll. (Hinweis: Der write()-Callable wird nur zur Unterstützung bestimmter imperativer Ausgabe-APIs bestehender Frameworks bereitgestellt; er sollte von neuen Anwendungen oder Frameworks nicht verwendet werden, wenn er vermieden werden kann. Siehe den Abschnitt Buffering and Streaming für weitere Details.)
Wenn er vom Server aufgerufen wird, muss das Anwendungsobjekt ein Iterable zurückgeben, das null oder mehr Strings liefert. Dies kann auf verschiedene Weise erreicht werden, z. B. durch Rückgabe einer Liste von Strings, oder indem die Anwendung eine Generatorfunktion ist, die Strings liefert, oder indem die Anwendung eine Klasse ist, deren Instanzen iterierbar sind. Unabhängig davon, wie es erreicht wird, muss das Anwendungsobjekt immer ein Iterable zurückgeben, das null oder mehr Strings liefert.
Der Server oder das Gateway muss die gelieferten Strings unbuffered an den Client übertragen, wobei die Übertragung jedes Strings abgeschlossen werden muss, bevor ein weiterer angefordert wird. (Mit anderen Worten, Anwendungen **sollten** ihr eigenes Pufferung durchführen. Siehe den Abschnitt Buffering and Streaming unten für weitere Informationen darüber, wie die Ausgabe von Anwendungen behandelt werden muss.)
Der Server oder das Gateway sollte die gelieferten Strings als binäre Byte-Sequenzen behandeln: Insbesondere sollte er sicherstellen, dass Zeilenumbrüche nicht geändert werden. Die Anwendung ist dafür verantwortlich, sicherzustellen, dass die zu schreibenden Strings im richtigen Format für den Client vorliegen. (Der Server oder das Gateway **kann** HTTP-Übertragungscodierungen anwenden oder andere Transformationen zur Implementierung von HTTP-Funktionen wie Byte-Range-Übertragung durchführen. Siehe Other HTTP Features unten für weitere Details.)
Wenn ein Aufruf von len(iterable) erfolgreich ist, muss der Server sich auf das Ergebnis verlassen können. Das heißt, wenn das von der Anwendung zurückgegebene Iterable eine funktionierende __len__()-Methode bereitstellt, **muss** sie ein genaues Ergebnis zurückgeben. (Siehe den Abschnitt Handling the Content-Length Header für Informationen darüber, wie dies normalerweise verwendet werden würde.)
Wenn das von der Anwendung zurückgegebene Iterable eine close()-Methode hat, **muss** der Server oder das Gateway diese Methode nach Abschluss der aktuellen Anfrage aufrufen, unabhängig davon, ob die Anfrage normal abgeschlossen wurde oder aufgrund eines Fehlers vorzeitig beendet wurde (dies dient der Unterstützung der Ressourcenfreigabe durch die Anwendung). Dieses Protokoll soll die Generator-Unterstützung von PEP 325 und andere gängige Iterables mit close()-Methoden ergänzen.
(Hinweis: Die Anwendung **muss** den start_response()-Callable aufrufen, bevor das Iterable den ersten Body-String liefert, damit der Server die Header vor jeglichem Body-Inhalt senden kann. Dieser Aufruf **kann** jedoch bei der ersten Iteration des Iterables erfolgen, daher **dürfen** Server nicht davon ausgehen, dass start_response() aufgerufen wurde, bevor sie mit der Iteration über das Iterable beginnen.)
Schließlich **dürfen** Server und Gateways keine anderen Attribute des von der Anwendung zurückgegebenen Iterables direkt verwenden, es sei denn, es handelt sich um eine Instanz eines Typs, der für diesen Server oder dieses Gateway spezifisch ist, wie z. B. ein „File Wrapper“, der von wsgi.file_wrapper zurückgegeben wird (siehe Optional Platform-Specific File Handling). Im allgemeinen Fall sind nur hier spezifizierte Attribute oder über z. B. die PEP 234-Iterator-APIs zugängliche Attribute zulässig.
environ-Variablen
Das environ-Dictionary muss diese CGI-Umgebungsvariablen enthalten, wie in der Common Gateway Interface-Spezifikation [2] definiert. Die folgenden Variablen **müssen** vorhanden sein, es sei denn, ihr Wert wäre ein leerer String, in welchem Fall sie **weggelassen werden können**, außer wie unten anders angegeben.
REQUEST_METHOD- Die HTTP-Anfragemethode, wie z. B.
"GET"oder"POST". Dies kann niemals ein leerer String sein und ist daher immer erforderlich. SCRIPT_NAME- Der Anfangsteil des Pfades der Anforderungs-URL, der dem Anwendungsobjekt entspricht, so dass die Anwendung seine virtuelle „Position“ kennt. Dieser **kann** ein leerer String sein, wenn die Anwendung der „Wurzel“ des Servers entspricht.
PATH_INFO- Der Rest des Pfades der Anforderungs-URL, der die virtuelle „Position“ des Ziels der Anfrage innerhalb der Anwendung bezeichnet. Dieser **kann** ein leerer String sein, wenn die URL die Anwendungs-Wurzel adressiert und keinen abschließenden Schrägstrich hat.
QUERY_STRING- Der Teil der Anforderungs-URL, der nach dem
"?"kommt, falls vorhanden. Kann leer oder abwesend sein. CONTENT_TYPE- Der Inhalt aller
Content-Type-Felder in der HTTP-Anfrage. Kann leer oder abwesend sein. CONTENT_LENGTH- Der Inhalt aller
Content-Length-Felder in der HTTP-Anfrage. Kann leer oder abwesend sein. SERVER_NAME,SERVER_PORT- In Kombination mit
SCRIPT_NAMEundPATH_INFOkönnen diese Variablen verwendet werden, um die URL zu vervollständigen. Beachten Sie jedoch, dassHTTP_HOST, falls vorhanden, vorSERVER_NAMEzur Rekonstruktion der Anforderungs-URL verwendet werden sollte. Siehe den Abschnitt URL Reconstruction unten für weitere Details.SERVER_NAMEundSERVER_PORTkönnen niemals leere Strings sein und sind daher immer erforderlich. SERVER_PROTOCOL- Die Version des Protokolls, das der Client zum Senden der Anfrage verwendet hat. Typischerweise ist dies etwas wie
"HTTP/1.0"oder"HTTP/1.1"und kann von der Anwendung verwendet werden, um zu bestimmen, wie HTTP-Anfrage-Header behandelt werden sollen. (Diese Variable sollte wahrscheinlichREQUEST_PROTOCOLgenannt werden, da sie das in der Anfrage verwendete Protokoll bezeichnet und nicht notwendigerweise das Protokoll ist, das in der Antwort des Servers verwendet wird. Aus Kompatibilitätsgründen mit CGI müssen wir jedoch den bestehenden Namen beibehalten.) HTTP_-Variablen- Variablen, die den vom Client bereitgestellten HTTP-Anfrage-Headern entsprechen (d. h. Variablen, deren Namen mit
"HTTP_"beginnen). Das Vorhandensein oder Fehlen dieser Variablen sollte dem Vorhandensein oder Fehlen des entsprechenden HTTP-Headers in der Anfrage entsprechen.
Ein Server oder Gateway **sollte** versuchen, so viele weitere CGI-Variablen wie zutreffend bereitzustellen. Wenn SSL verwendet wird, **sollte** der Server oder das Gateway auch so viele der Apache SSL-Umgebungsvariablen [3] wie zutreffend bereitstellen, wie z. B. HTTPS=on und SSL_PROTOCOL. Beachten Sie jedoch, dass eine Anwendung, die andere CGI-Variablen als die oben genannten verwendet, zwangsläufig nicht portabel zu Webservern ist, die die entsprechenden Erweiterungen nicht unterstützen. (Zum Beispiel können Webserver, die keine Dateien veröffentlichen, kein sinnvolles DOCUMENT_ROOT oder PATH_TRANSLATED bereitstellen.)
Ein WSGI-konformer Server oder Gateway **sollte** dokumentieren, welche Variablen er zusammen mit ihren Definitionen bereitstellt. Anwendungen **sollten** auf das Vorhandensein der von ihnen benötigten Variablen prüfen und einen Ausweichplan für den Fall erstellen, dass eine solche Variable fehlt.
Hinweis: Fehlende Variablen (wie z. B. REMOTE_USER, wenn keine Authentifizierung stattgefunden hat) sollten aus dem environ-Dictionary weggelassen werden. Beachten Sie auch, dass CGI-definierte Variablen Strings sein müssen, wenn sie überhaupt vorhanden sind. Es verstößt gegen diese Spezifikation, wenn der Wert einer CGI-Variable einen anderen Typ als str hat.
Zusätzlich zu den CGI-definierten Variablen **kann** das environ-Dictionary auch beliebige Betriebssystem-„Umgebungsvariablen“ enthalten und **muss** die folgenden WSGI-definierten Variablen enthalten:
| Variable. | Wert |
|---|---|
wsgi.version |
Das Tupel (1, 0), das WSGI-Version 1.0 darstellt. |
wsgi.url_scheme |
Ein String, der den „scheme“-Teil der URL repräsentiert, unter der die Anwendung aufgerufen wird. Normalerweise wird dies den Wert "http" oder "https" haben, je nach Bedarf. |
wsgi.input |
Ein Eingabestrom (dateiähnliches Objekt), von dem der HTTP-Anfragekörper gelesen werden kann. (Der Server oder das Gateway kann Leseoperationen bei Bedarf durchführen, wie von der Anwendung angefordert, oder es kann den Anfragekörper des Clients vorlesen und im Speicher oder auf der Festplatte puffern, oder jede andere Technik zur Bereitstellung eines solchen Eingabestroms verwenden, je nach Präferenz.) |
wsgi.errors |
Ein Ausgabestrom (dateiähnliches Objekt), in das Fehlerausgaben geschrieben werden können, um Programm- oder andere Fehler an einem standardisierten und möglicherweise zentralisierten Ort zu protokollieren. Dies sollte ein „Textmodus“-Stream sein; d. h. Anwendungen sollten "\n" als Zeilenende verwenden und davon ausgehen, dass es vom Server/Gateway in das richtige Zeilenende umgewandelt wird.Für viele Server ist |
wsgi.multithread |
Dieser Wert sollte wahr sein, wenn das Anwendungsobjekt gleichzeitig von einem anderen Thread im selben Prozess aufgerufen werden kann, und sollte andernfalls falsch sein. |
wsgi.multiprocess |
Dieser Wert sollte wahr sein, wenn ein gleichwertiges Anwendungsobjekt gleichzeitig von einem anderen Prozess aufgerufen werden kann, und sollte andernfalls falsch sein. |
wsgi.run_once |
Dieser Wert sollte wahr sein, wenn der Server oder das Gateway erwartet (aber nicht garantiert!), dass die Anwendung während der Lebensdauer ihres enthaltenden Prozesses nur einmal aufgerufen wird. Normalerweise ist dies nur bei einem Gateway der Fall, das auf CGI (oder etwas Ähnlichem) basiert. |
Schließlich kann das environ-Dictionary auch serverspezifische Variablen enthalten. Diese Variablen sollten nur Kleinbuchstaben, Zahlen, Punkte und Unterstriche verwenden und mit einem Namen präfixiert sein, der für den definierenden Server oder das definierende Gateway eindeutig ist. Zum Beispiel könnte mod_python Variablen mit Namen wie mod_python.some_variable definieren.
Ein- und Fehlerströme
Die vom Server bereitgestellten Eingabe- und Fehlerströme müssen die folgenden Methoden unterstützen:
| Methode | Stream | Anmerkungen |
|---|---|---|
read(size) |
input |
1 |
readline() |
input |
1, 2 |
readlines(hint) |
input |
1, 3 |
__iter__() |
input |
|
flush() |
errors |
4 |
write(str) |
errors |
|
writelines(seq) |
errors |
Die Semantik jeder Methode ist wie in der Python Library Reference dokumentiert, außer für diese Hinweise, die in der obigen Tabelle aufgeführt sind:
- Der Server ist nicht verpflichtet, über die vom Client angegebene
Content-Lengthhinaus zu lesen, und darf eine End-of-File-Bedingung simulieren, wenn die Anwendung versucht, über diesen Punkt hinaus zu lesen. Die Anwendung **sollte nicht** versuchen, mehr Daten zu lesen, als durch die VariableCONTENT_LENGTHangegeben ist. - Das optionale „size“-Argument für
readline()wird nicht unterstützt, da es für Server-Autoren komplex zu implementieren sein kann und in der Praxis selten verwendet wird. - Beachten Sie, dass das
hint-Argument fürreadlines()sowohl für den Aufrufer als auch für den Implementierer optional ist. Die Anwendung ist frei, es nicht bereitzustellen, und der Server oder das Gateway ist frei, es zu ignorieren. - Da der
errors-Stream nicht zurückgespult werden kann, sind Server und Gateways frei, Schreiboperationen sofort weiterzuleiten, ohne zu puffern. In diesem Fall kann dieflush()-Methode ein No-Op sein. Portierbare Anwendungen können jedoch nicht davon ausgehen, dass die Ausgabe unbuffered ist oder dassflush()ein No-Op ist. Sie müssenflush()aufrufen, wenn sie sicherstellen müssen, dass die Ausgabe tatsächlich geschrieben wurde. (Zum Beispiel, um die Vermischung von Daten aus mehreren Prozessen zu minimieren, die in dasselbe Fehlerprotokoll schreiben.)
Die in der obigen Tabelle aufgeführten Methoden **müssen** von allen dieser Spezifikation entsprechenden Servern unterstützt werden. Anwendungen, die dieser Spezifikation entsprechen, **dürfen** keine anderen Methoden oder Attribute der input- oder errors-Objekte verwenden. Insbesondere **dürfen** Anwendungen nicht versuchen, diese Streams zu schließen, auch wenn sie close()-Methoden besitzen.
Der start_response() Callable
Der zweite Parameter, der an das Anwendungsobjekt übergeben wird, ist ein aufrufbares Objekt der Form start_response(status, response_headers, exc_info=None). (Wie bei allen WSGI-Callables müssen die Argumente positionsweise und nicht per Schlüsselwort übergeben werden.) Der start_response-Callable wird verwendet, um die HTTP-Antwort zu beginnen, und er muss einen write(body_data)-Callable zurückgeben (siehe den Abschnitt Buffering and Streaming unten).
Das status-Argument ist ein HTTP-„Status“-String wie "200 OK" oder "404 Not Found". Das heißt, es ist ein String, der aus einem Status-Code und einer Reason-Phrase besteht, in dieser Reihenfolge und durch ein einzelnes Leerzeichen getrennt, ohne umschließende Leerzeichen oder andere Zeichen. (Siehe RFC 2616, Abschnitt 6.1.1 für weitere Informationen.) Der String **darf keine** Steuerzeichen enthalten und darf nicht mit einem Wagenrücklauf, Zeilenvorschub oder einer Kombination davon abgeschlossen werden.
Das Argument response_headers ist eine Liste von Tupeln (header_name, header_value). Es muss eine Python-Liste sein; d. h. type(response_headers) is ListType, und der Server **darf** dessen Inhalt nach Belieben ändern. Jeder header_name muss ein gültiger HTTP-Header-Feldname sein (wie in RFC 2616, Abschnitt 4.2 definiert), ohne einen abschließenden Doppelpunkt oder andere Satzzeichen.
Jeder header_value **darf keine** *irgendwelchen* Steuerzeichen enthalten, einschließlich Wagenrückläufen oder Zeilenvorschüben, weder eingebettet noch am Ende. (Diese Anforderungen dienen dazu, die Komplexität der Parsierung zu minimieren, die von Servern, Gateways und zwischengeschalteten Antwortverarbeitern durchgeführt werden muss, die Antwortheader inspizieren oder ändern müssen.)
Im Allgemeinen ist der Server oder das Gateway dafür verantwortlich, sicherzustellen, dass korrekte Header an den Client gesendet werden: Wenn die Anwendung einen von HTTP (oder anderen relevanten Spezifikationen, die in Kraft sind) geforderten Header weglässt, **muss** der Server oder das Gateway ihn hinzufügen. Zum Beispiel würden die HTTP-Header Date: und Server: normalerweise vom Server oder Gateway bereitgestellt werden.
(Eine Erinnerung für Server/Gateway-Autoren: HTTP-Header-Namen sind nicht case-sensitiv, also stellen Sie sicher, dass Sie das bei der Überprüfung anwendungsseitig bereitgestellter Header berücksichtigen!)
Anwendungen und Middleware dürfen keine „hop-by-hop“-Funktionen oder Header von HTTP/1.1, keine gleichwertigen Funktionen in HTTP/1.0 oder irgendwelche Header verwenden, die die Persistenz der Client-Verbindung zum Webserver beeinträchtigen würden. Diese Funktionen sind das alleinige Hoheitsgebiet des eigentlichen Webservers, und ein Server oder Gateway **sollte** es als fatalen Fehler betrachten, wenn eine Anwendung versucht, diese zu senden, und einen Fehler auslösen, wenn sie an start_response() übergeben werden. (Weitere Details zu „hop-by-hop“-Funktionen und Headern finden Sie im Abschnitt Other HTTP Features unten.)
Die start_response-Aufrufe dürfen die Antwortheader nicht tatsächlich übertragen. Stattdessen müssen sie vom Server oder Gateway gespeichert werden, um erst nach der ersten Iteration des Rückgabewerts der Anwendung, der einen nicht leeren String liefert, oder nach dem ersten Aufruf der write()-Aufrufe durch die Anwendung übertragen zu werden. Mit anderen Worten, Antwortheader dürfen erst gesendet werden, wenn tatsächliche Body-Daten verfügbar sind oder die von der Anwendung zurückgegebene iterierbare Sequenz erschöpft ist. (Die einzige mögliche Ausnahme von dieser Regel ist, wenn die Antwortheader explizit einen Content-Length von Null enthalten.)
Dieses Verzögern der Übertragung von Antwortheadern dient dazu, dass gepufferte und asynchrone Anwendungen ihre ursprünglich beabsichtigten Ausgaben bis zum letzten möglichen Moment durch Fehlerausgaben ersetzen können. Beispielsweise muss die Anwendung möglicherweise den Antwortstatus von "200 OK" auf "500 Internal Error" ändern, wenn ein Fehler auftritt, während der Body innerhalb eines Anwendungsbuffers generiert wird.
Das Argument exc_info, falls übergeben, muss ein Python- sys.exc_info() Tupel sein. Dieses Argument sollte von der Anwendung nur dann übergeben werden, wenn start_response von einem Fehlerbehandlungsroutine aufgerufen wird. Wenn exc_info übergeben wird und noch keine HTTP-Header ausgegeben wurden, sollte start_response die aktuell gespeicherten HTTP-Antwortheader durch die neu bereitgestellten ersetzen, wodurch die Anwendung ihre Meinung zur Ausgabe ändern kann, wenn ein Fehler aufgetreten ist.
Wenn jedoch exc_info übergeben wird und die HTTP-Header bereits gesendet wurden, muss start_response einen Fehler auslösen und sollte das exc_info Tupel auslösen. Das heißt
raise exc_info[0], exc_info[1], exc_info[2]
Dies wird die von der Anwendung abgefangene Ausnahme erneut auslösen und sollte prinzipiell die Anwendung abbrechen. (Es ist nicht sicher für die Anwendung, Fehler an den Browser auszugeben, sobald die HTTP-Header bereits gesendet wurden.) Die Anwendung darf keine Ausnahmen abfangen, die von start_response ausgelöst werden, wenn sie start_response mit exc_info aufgerufen hat. Stattdessen sollte sie zulassen, dass solche Ausnahmen an den Server oder das Gateway weitergegeben werden. Weitere Details finden Sie weiter unten unter Fehlerbehandlung.
Die Anwendung kann start_response mehr als einmal aufrufen, wenn und nur wenn das Argument exc_info übergeben wird. Genauer gesagt ist es ein fataler Fehler, start_response ohne das Argument exc_info aufzurufen, wenn start_response bereits im aktuellen Aufruf der Anwendung aufgerufen wurde. (Siehe das obige Beispiel für ein CGI-Gateway zur Veranschaulichung der korrekten Logik.)
Hinweis: Server, Gateways oder Middleware, die start_response implementieren, sollten sicherstellen, dass keine Referenz auf den exc_info-Parameter über die Dauer der Ausführung der Funktion hinaus gehalten wird, um eine zirkuläre Referenz über den Traceback und die beteiligten Frames zu vermeiden. Der einfachste Weg dazu ist etwas wie
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
# do stuff w/exc_info here
finally:
exc_info = None # Avoid circular ref.
Das Beispiel für ein CGI-Gateway bietet eine weitere Veranschaulichung dieser Technik.
Behandlung des Content-Length Headers
Wenn die Anwendung keinen Content-Length-Header liefert, kann ein Server oder Gateway einen von mehreren Ansätzen zur Handhabung wählen. Der einfachste davon ist, die Client-Verbindung zu schließen, wenn die Antwort abgeschlossen ist.
Unter bestimmten Umständen kann der Server oder das Gateway jedoch entweder einen Content-Length-Header generieren oder zumindest die Notwendigkeit vermeiden, die Client-Verbindung zu schließen. Wenn die Anwendung die write()-Aufrufe nicht aufruft und eine iterierbare Sequenz zurückgibt, deren len() 1 ist, dann kann der Server Content-Length automatisch bestimmen, indem er die Länge des ersten von der iterierbaren Sequenz gelieferten Strings nimmt.
Und wenn Server und Client beide HTTP/1.1 unterstützen „Chunked Encoding“, dann kann der Server Chunked Encoding verwenden, um für jeden write()-Aufruf oder jeden von der iterierbaren Sequenz gelieferten String einen Chunk zu senden, wodurch für jeden Chunk ein Content-Length-Header generiert wird. Dies ermöglicht es dem Server, die Client-Verbindung offen zu halten, wenn er dies wünscht. Beachten Sie, dass der Server dabei vollständig RFC 2616 einhalten muss, oder auf eine der anderen Strategien für den Umgang mit der Abwesenheit von Content-Length zurückfallen muss.
(Hinweis: Anwendungen und Middleware dürfen keine Art von Transfer-Encoding auf ihre Ausgabe anwenden, wie z. B. Chunking oder Gzipping; da diese Kodierungen "hop-by-hop"-Operationen sind, sind sie Sache des eigentlichen Webservers/Gateways. Weitere Details finden Sie weiter unten unter Andere HTTP-Features.)
Puffern und Streaming
Generell erzielen Anwendungen den besten Durchsatz, indem sie ihre (moderat großen) Ausgaben puffern und sie auf einmal senden. Dies ist ein üblicher Ansatz in bestehenden Frameworks wie Zope: Die Ausgabe wird in einem StringIO- oder ähnlichen Objekt gepuffert und dann zusammen mit den Antwortheadern auf einmal übertragen.
Der entsprechende Ansatz in WSGI besteht darin, dass die Anwendung einfach eine iterierbare Sequenz mit einem einzigen Element (wie z. B. einer Liste) zurückgibt, die den Antwortbody als einzelnen String enthält. Dies ist der empfohlene Ansatz für die überwiegende Mehrheit der Anwendungsfunktionen, die HTML-Seiten rendern, deren Text leicht in den Speicher passt.
Für große Dateien oder für spezialisierte Anwendungen von HTTP-Streaming (wie z. B. Multipart "Server Push") muss eine Anwendung jedoch möglicherweise die Ausgabe in kleineren Blöcken bereitstellen (z. B. um zu vermeiden, eine große Datei in den Speicher zu laden). Es kommt auch manchmal vor, dass die Erstellung eines Teils einer Antwort zeitaufwändig ist, aber es wäre nützlich, den Teil der Antwort, der davor liegt, vorab zu senden.
In diesen Fällen gibt die Anwendung normalerweise eine Iteratorin (oft eine Generator-Iteratorin) zurück, die die Ausgabe blockweise liefert. Diese Blöcke können an Multipart-Grenzen (für "Server Push") oder kurz vor zeitaufwändigen Aufgaben (wie dem Lesen eines weiteren Blocks einer Datei auf der Festplatte) unterteilt werden.
WSGI-Server, Gateways und Middleware dürfen die Übertragung eines Blocks nicht verzögern; sie müssen entweder den gesamten Block an den Client übertragen oder garantieren, dass sie die Übertragung fortsetzen, auch während die Anwendung ihren nächsten Block generiert. Ein Server/Gateway oder Middleware kann diese Garantie auf drei Arten erbringen:
- Den gesamten Block an das Betriebssystem senden (und anfordern, dass alle Puffer des Betriebssystems geleert werden), bevor die Kontrolle an die Anwendung zurückgegeben wird, ODER
- Einen anderen Thread verwenden, um sicherzustellen, dass die Übertragung des Blocks fortgesetzt wird, während die Anwendung den nächsten Block generiert.
- (Nur Middleware) Den gesamten Block an sein übergeordnetes Gateway/Server senden
Durch die Bereitstellung dieser Garantie ermöglicht WSGI Anwendungen, sicherzustellen, dass die Übertragung nicht an einer beliebigen Stelle in ihren Ausgabedaten ins Stocken gerät. Dies ist entscheidend für das ordnungsgemäße Funktionieren von z. B. Multipart "Server Push"-Streaming, bei dem Daten zwischen Multipart-Grenzen vollständig an den Client übertragen werden sollten.
Middleware-Behandlung von Blockgrenzen
Um asynchrone Anwendungen und Server besser zu unterstützen, dürfen Middleware-Komponenten die Iteration nicht blockieren, während sie auf mehrere Werte von einer Anwendungs-Iterierbarkeit warten. Wenn die Middleware mehr Daten von der Anwendung sammeln muss, bevor sie eine Ausgabe liefern kann, muss sie einen leeren String liefern.
Anders ausgedrückt muss eine Middleware-Komponente mindestens einen Wert liefern, jedes Mal wenn ihre zugrunde liegende Anwendung einen Wert liefert. Wenn die Middleware keinen anderen Wert liefern kann, muss sie einen leeren String liefern.
Diese Anforderung stellt sicher, dass asynchrone Anwendungen und Server zusammenarbeiten können, um die Anzahl der Threads zu reduzieren, die benötigt werden, um eine bestimmte Anzahl von Anwendungsinstanzen gleichzeitig auszuführen.
Beachten Sie auch, dass diese Anforderung bedeutet, dass Middleware eine iterierbare Sequenz zurückgeben muss, sobald ihre zugrunde liegende Anwendung eine iterierbare Sequenz zurückgibt. Es ist auch verboten für Middleware, die write()-Aufrufe zu verwenden, um Daten zu übertragen, die von einer zugrunde liegenden Anwendung geliefert werden. Middleware darf nur die write()-Aufrufe ihres übergeordneten Servers verwenden, um Daten zu übertragen, die die zugrunde liegende Anwendung über eine von der Middleware bereitgestellte write()-Aufruf geliefert hat.
Der write() Callable
Einige bestehende APIs von Anwendungsframeworks unterstützen unbuffered Ausgabe auf eine andere Weise als WSGI. Insbesondere stellen sie eine "write"-Funktion oder -Methode irgendeiner Art bereit, um einen unbuffered Datenblock zu schreiben, oder sie stellen eine buffered "write"-Funktion und einen "flush"-Mechanismus zum Leeren des Buffers bereit.
Unglücklicherweise können solche APIs nicht auf der Grundlage des WSGI-Rückgabewerts "iterierbare Sequenz" implementiert werden, es sei denn, Threads oder andere spezielle Mechanismen werden verwendet.
Daher enthält WSGI, um diesen Frameworks die fortgesetzte Verwendung einer imperativen API zu ermöglichen, einen speziellen write()-Aufruf, der von der start_response-Aufrufe zurückgegeben wird.
Neue WSGI-Anwendungen und -Frameworks sollten die write()-Aufrufe nicht verwenden, wenn es vermieden werden kann. Die write()-Aufrufe sind streng genommen ein Hack zur Unterstützung imperativer Streaming-APIs. Im Allgemeinen sollten Anwendungen ihre Ausgabe über ihre zurückgegebene iterierbare Sequenz erzeugen, da dies es Webservern ermöglicht, andere Aufgaben im selben Python-Thread zu verschachteln, was potenziell einen besseren Durchsatz für den Server als Ganzes bietet.
Die write()-Aufrufe wird von der start_response()-Aufrufe zurückgegeben und akzeptiert einen einzelnen Parameter: einen String, der als Teil des HTTP-Antwortbodys geschrieben wird und genauso behandelt wird, als ob er von der Ausgabe-Iterierbarkeit geliefert worden wäre. Mit anderen Worten, bevor write() zurückkehrt, muss sie garantieren, dass der übergebene String entweder vollständig an den Client gesendet wurde oder für die Übertragung gepuffert ist, während die Anwendung weitermacht.
Eine Anwendung muss ein iterierbares Objekt zurückgeben, auch wenn sie write() verwendet, um den gesamten oder einen Teil ihres Antwortbodys zu erzeugen. Die zurückgegebene iterierbare Sequenz kann leer sein (d. h. keine nicht leeren Strings liefern), aber wenn sie nicht leere Strings liefert, muss diese Ausgabe normal vom Server oder Gateway behandelt werden (d. h. sie muss sofort gesendet oder in die Warteschlange gestellt werden). Anwendungen dürfen write() nicht aus ihrer Rückgabe-Iterierbarkeit aufrufen, und daher werden alle von der Iterierbarkeit gelieferten Strings nach der Übertragung aller an write() übergebenen Strings an den Client übertragen.
Unicode-Probleme
HTTP unterstützt Unicode nicht direkt, und diese Schnittstelle auch nicht. Die gesamte Kodierung/Dekodierung muss von der Anwendung gehandhabt werden; alle Strings, die an oder von dem Server übergeben werden, müssen Standard-Python-Byte-Strings sein, keine Unicode-Objekte. Das Ergebnis der Verwendung eines Unicode-Objekts, wo ein String-Objekt erforderlich ist, ist undefiniert.
Beachten Sie auch, dass Strings, die als Status oder Antwortheader an start_response() übergeben werden, RFC 2616 in Bezug auf die Kodierung befolgen müssen. Das heißt, sie müssen entweder ISO-8859-1-Zeichen sein oder RFC 2047 MIME-Kodierung verwenden.
Auf Python-Plattformen, auf denen der Typ str oder StringType tatsächlich Unicode-basiert ist (z. B. Jython, IronPython, Python 3000 usw.), müssen alle in dieser Spezifikation genannten "Strings" nur Codepunkte enthalten, die in der ISO-8859-1-Kodierung darstellbar sind (\u0000 bis \u00FF, einschließlich). Es ist ein fataler Fehler für eine Anwendung, Strings mit anderen Unicode-Zeichen oder Codepunkten zu liefern. Ebenso dürfen Server und Gateways keine Strings an eine Anwendung liefern, die andere Unicode-Zeichen enthalten.
Auch hier müssen alle in dieser Spezifikation genannten Strings vom Typ str oder StringType sein und dürfen nicht vom Typ unicode oder UnicodeType sein. Und selbst wenn eine bestimmte Plattform mehr als 8 Bits pro Zeichen in str/StringType-Objekten zulässt, dürfen nur die unteren 8 Bits verwendet werden, für jeden Wert, der in dieser Spezifikation als "String" bezeichnet wird.
Fehlerbehandlung
Im Allgemeinen sollten Anwendungen versuchen, ihre eigenen internen Fehler abzufangen und eine hilfreiche Nachricht im Browser anzuzeigen. (Es liegt an der Anwendung zu entscheiden, was "hilfreich" in diesem Zusammenhang bedeutet.)
Um eine solche Nachricht anzuzeigen, darf die Anwendung jedoch noch keine Daten an den Browser gesendet haben, da sie sonst Gefahr läuft, die Antwort zu beschädigen. WSGI bietet daher einen Mechanismus, um der Anwendung entweder die Übertragung ihrer Fehlermeldung zu ermöglichen oder automatisch abgebrochen zu werden: das Argument exc_info für start_response. Hier ist ein Beispiel für dessen Verwendung
try:
# regular application code here
status = "200 Froody"
response_headers = [("content-type", "text/plain")]
start_response(status, response_headers)
return ["normal body goes here"]
except:
# XXX should trap runtime issues like MemoryError, KeyboardInterrupt
# in a separate handler before this bare 'except:'...
status = "500 Oops"
response_headers = [("content-type", "text/plain")]
start_response(status, response_headers, sys.exc_info())
return ["error body goes here"]
Wenn keine Ausgabe geschrieben wurde, wenn ein Fehler auftritt, wird der Aufruf von start_response normal zurückkehren, und die Anwendung gibt einen Fehler-Body zurück, der an den Browser gesendet werden soll. Wenn jedoch bereits eine Ausgabe an den Browser gesendet wurde, wird start_response die bereitgestellte Ausnahme erneut auslösen. Diese Ausnahme sollte nicht von der Anwendung abgefangen werden, und so wird die Anwendung abgebrochen. Der Server oder das Gateway kann dann diese (fatale) Ausnahme abfangen und die Antwort abbrechen.
Server sollten Ausnahmen abfangen und protokollieren, die eine Anwendung oder die Iteration ihres Rückgabewerts abbrechen. Wenn eine teilweise Antwort bereits an den Browser geschrieben wurde, wenn ein Anwendungsfehler auftritt, kann der Server oder das Gateway versuchen, eine Fehlermeldung zur Ausgabe hinzuzufügen, wenn die bereits gesendeten Header einen text/* Content-Type angeben, den der Server sauber modifizieren kann.
Einige Middleware möchten möglicherweise zusätzliche Fehlerbehandlungsdienste anbieten oder Anwendungsfehlermeldungen abfangen und ersetzen. In solchen Fällen kann Middleware wählen, die an start_response übergebene exc_info nicht erneut auszulösen, sondern stattdessen eine Middleware-spezifische Ausnahme auszulösen oder einfach ohne Ausnahme zurückzukehren, nachdem die übergebenen Argumente gespeichert wurden. Dies führt dann dazu, dass die Anwendung ihren Fehler-Body-Iterable zurückgibt (oder write() aufruft), wodurch die Middleware die Fehler-Ausgabe erfassen und modifizieren kann. Diese Techniken funktionieren, solange die Anwendungsautoren
- Immer
exc_infoangeben, wenn eine Fehlerantwort begonnen wird - Niemals Fehler abfangen, die von
start_responseausgelöst werden, wennexc_infoangegeben wird
HTTP 1.1 Expect/Continue
Server und Gateways, die HTTP 1.1 implementieren, müssen transparente Unterstützung für den "Expect/Continue"-Mechanismus von HTTP 1.1 bieten. Dies kann auf verschiedene Arten erfolgen:
- Auf Anfragen mit
Expect: 100-continuemit einer sofortigen "100 Continue"-Antwort reagieren und normal fortfahren. - Mit der Anfrage normal fortfahren, aber der Anwendung einen
wsgi.input-Stream bereitstellen, der die "100 Continue"-Antwort sendet, wenn/wenn die Anwendung versucht, aus dem Eingabestrom zu lesen. Die Leseanfrage muss dann blockiert bleiben, bis der Client antwortet. - Warten, bis der Client entscheidet, dass der Server Expect/Continue nicht unterstützt und den Anfrage-Body selbst sendet. (Dies ist suboptimal und nicht empfehlenswert.)
Beachten Sie, dass diese Verhaltensbeschränkungen nicht für HTTP 1.0-Anfragen oder für Anfragen gelten, die nicht an ein Anwendungsobjekt gerichtet sind. Weitere Informationen zu HTTP 1.1 Expect/Continue finden Sie in RFC 2616, Abschnitte 8.2.3 und 10.1.1.
Andere HTTP-Funktionen
Im Allgemeinen sollten Server und Gateways "dumm spielen" und der Anwendung die vollständige Kontrolle über ihre Ausgabe überlassen. Sie sollten nur Änderungen vornehmen, die die effektive Semantik der Antwort der Anwendung nicht verändern. Es ist immer möglich, dass der Anwendungsentwickler Middleware-Komponenten hinzufügt, um zusätzliche Funktionen bereitzustellen, daher sollten Server-/Gateway-Entwickler bei ihrer Implementierung konservativ sein. In gewissem Sinne sollte sich ein Server wie ein HTTP-"Gateway-Server" betrachten, wobei die Anwendung ein HTTP-"Origin-Server" ist. (Siehe RFC 2616, Abschnitt 1.3, für die Definition dieser Begriffe.)
Da WSGI-Server und -Anwendungen jedoch nicht über HTTP kommunizieren, gelten die in RFC 2616 als "hop-by-hop"-Header bezeichneten Header nicht für die WSGI-internen Kommunikationen. WSGI-Anwendungen dürfen keine "hop-by-hop"-Header generieren, keine HTTP-Features verwenden, die sie zur Generierung solcher Header benötigen würden, oder sich auf den Inhalt eingehender "hop-by-hop"-Header im environ-Dictionary verlassen. WSGI-Server müssen unterstützte eingehende "hop-by-hop"-Header selbst behandeln, z. B. durch Dekodierung von eingehenden Transfer-Encoding, einschließlich Chunked Encoding, falls zutreffend.
Wenn man diese Prinzipien auf eine Vielzahl von HTTP-Features anwendet, sollte klar sein, dass ein Server die Cache-Validierung über die If-None-Match und If-Modified-Since-Anfrageheader und die Last-Modified und ETag-Antwortheader handhaben kann. Dies ist jedoch nicht erforderlich, und die Anwendung sollte ihre eigene Cache-Validierung durchführen, wenn sie diese Funktion unterstützen möchte, da der Server/Gateway eine solche Validierung nicht durchführen muss.
Ähnlich kann ein Server die Antwort einer Anwendung neu kodieren oder transportkodieren, aber die Anwendung sollte selbst eine geeignete Inhaltskodierung verwenden und darf keine Transportkodierung anwenden. Ein Server kann Byte-Bereiche der Antwort der Anwendung übertragen, wenn diese vom Client angefordert werden und die Anwendung Byte-Bereiche nicht nativ unterstützt. Auch hier sollte die Anwendung diese Funktion jedoch selbst durchführen, wenn gewünscht.
Beachten Sie, dass diese Einschränkungen für Anwendungen nicht unbedingt bedeuten, dass jede Anwendung jede HTTP-Funktion neu implementieren muss; viele HTTP-Funktionen können teilweise oder vollständig von Middleware-Komponenten implementiert werden, wodurch sowohl Server- als auch Anwendungsautoren von der wiederholten Implementierung derselben Funktionen befreit werden.
Thread-Unterstützung
Die Thread-Unterstützung oder deren Fehlen ist ebenfalls serverabhängig. Server, die mehrere Anfragen parallel ausführen können, sollten auch die Option bieten, eine Anwendung im Single-Thread-Modus auszuführen, damit Anwendungen oder Frameworks, die nicht Thread-sicher sind, auch mit diesem Server verwendet werden können.
Implementierungs-/Anwendungsnotizen
Server-Erweiterungs-APIs
Einige Serverautoren möchten möglicherweise erweiterte APIs bereitstellen, die Anwendungs- oder Framework-Autoren für spezialisierte Zwecke nutzen können. Beispielsweise könnte ein Gateway, das auf mod_python basiert, einen Teil der Apache-API als WSGI-Erweiterung bereitstellen.
Im einfachsten Fall erfordert dies nichts weiter als die Definition einer environ-Variablen, z. B. mod_python.some_api. In vielen Fällen kann die mögliche Anwesenheit von Middleware dies jedoch erschweren. Beispielsweise könnte eine API, die Zugriff auf dieselben HTTP-Header bietet, wie sie in environ-Variablen zu finden sind, andere Daten zurückgeben, wenn environ von Middleware geändert wurde.
Im Allgemeinen birgt jede Erweiterungs-API, die einen Teil der WSGI-Funktionalität dupliziert, ersetzt oder umgeht, das Risiko, inkompatibel mit Middleware-Komponenten zu sein. Server-/Gateway-Entwickler sollten nicht davon ausgehen, dass niemand Middleware verwenden wird, da einige Framework-Entwickler speziell beabsichtigen, ihre Frameworks so zu organisieren oder neu zu organisieren, dass sie fast vollständig als Middleware verschiedener Art fungieren.
Um die Kompatibilität zu maximieren, müssen Server und Gateways, die Erweiterungs-APIs bereitstellen, die einen Teil der WSGI-Funktionalität ersetzen, diese APIs so gestalten, dass sie über den Teil der API aufgerufen werden, den sie ersetzen. Beispielsweise muss eine Erweiterungs-API für den Zugriff auf HTTP-Anfrage-Header erfordern, dass die Anwendung ihre aktuelle environ übergibt, damit der Server/Gateway überprüfen kann, ob über die API zugängliche HTTP-Header nicht von Middleware geändert wurden. Wenn die Erweiterungs-API nicht garantieren kann, dass sie mit environ in Bezug auf den Inhalt von HTTP-Headern immer übereinstimmt, muss sie den Dienst für die Anwendung verweigern, z. B. durch Auslösen eines Fehlers, Zurückgeben von None anstelle einer Header-Sammlung oder was auch immer für die API angemessen ist.
Ähnlich gilt, wenn eine Erweiterungs-API einen alternativen Weg zum Schreiben von Antwortdaten oder Headern bietet, sollte sie die start_response-Aufrufe übergeben lassen, bevor die Anwendung den erweiterten Dienst erhalten kann. Wenn das übergebene Objekt nicht dasselbe ist, das der Server/Gateway ursprünglich an die Anwendung geliefert hat, kann er keine korrekte Funktion garantieren und muss die Bereitstellung des erweiterten Dienstes für die Anwendung verweigern.
Diese Richtlinien gelten auch für Middleware, die Informationen wie geparste Cookies, Formulardaten, Sitzungen und dergleichen zu environ hinzufügt. Insbesondere sollte solche Middleware diese Funktionen als Funktionen bereitstellen, die auf environ operieren, anstatt Werte einfach in environ zu stopfen. Dies trägt dazu bei, dass Informationen aus environ berechnet werden, nachdem jede Middleware URL-Rewrites oder andere environ-Änderungen vorgenommen hat.
Es ist sehr wichtig, dass diese "sicheren Erweiterungsregeln" sowohl von Server-/Gateway- als auch von Middleware-Entwicklern befolgt werden, um eine Zukunft zu vermeiden, in der Middleware-Entwickler gezwungen sind, alle Erweiterungs-APIs aus environ zu löschen, um sicherzustellen, dass ihre Vermittlung nicht von Anwendungen umgangen wird, die diese Erweiterungen nutzen!
Anwendungskonfiguration
Diese Spezifikation definiert nicht, wie ein Server eine Anwendung auswählt oder erhält, um sie aufzurufen. Dies und andere Konfigurationsoptionen sind sehr serverspezifische Angelegenheiten. Es wird erwartet, dass Server-/Gateway-Autoren dokumentieren, wie der Server für die Ausführung eines bestimmten Anwendungsobjekts konfiguriert wird und mit welchen Optionen (wie Threading-Optionen).
Framework-Autoren hingegen sollten dokumentieren, wie ein Anwendungsobjekt erstellt wird, das ihre Framework-Funktionalität umschließt. Der Benutzer, der sowohl den Server als auch das Anwendungsframework gewählt hat, muss die beiden miteinander verbinden. Da jedoch sowohl das Framework als auch der Server nun eine gemeinsame Schnittstelle haben, sollte dies lediglich eine mechanische Angelegenheit sein und keine bedeutende technische Aufgabe für jedes neue Server-/Framework-Paar.
Schließlich möchten einige Anwendungen, Frameworks und Middleware möglicherweise das environ-Dictionary verwenden, um einfache String-Konfigurationsoptionen zu empfangen. Server und Gateways sollten dies unterstützen, indem sie es dem Bereitsteller einer Anwendung ermöglichen, Namens-Wert-Paare anzugeben, die in environ eingefügt werden sollen. Im einfachsten Fall kann diese Unterstützung lediglich darin bestehen, alle vom Betriebssystem bereitgestellten Umgebungsvariablen aus os.environ in das environ-Dictionary zu kopieren, da der Bereitsteller diese prinzipiell extern zum Server konfigurieren kann oder im CGI-Fall über die Konfigurationsdateien des Servers festgelegt werden können.
Anwendungen sollten versuchen, solche erforderlichen Variablen auf ein Minimum zu beschränken, da nicht alle Server eine einfache Konfiguration dafür unterstützen werden. Selbst im schlimmsten Fall können Personen, die eine Anwendung bereitstellen, natürlich ein Skript erstellen, um die erforderlichen Konfigurationswerte bereitzustellen
from the_app import application
def new_app(environ, start_response):
environ['the_app.configval1'] = 'something'
return application(environ, start_response)
Die meisten bestehenden Anwendungen und Frameworks benötigen wahrscheinlich nur einen einzigen Konfigurationswert aus environ, um den Speicherort ihrer anwendungs- oder frameworkspezifischen Konfigurationsdatei(en) anzugeben. (Natürlich sollten Anwendungen diese Konfiguration cachen, um zu vermeiden, sie bei jedem Aufruf erneut lesen zu müssen.)
URL-Rekonstruktion
Wenn eine Anwendung eine vollständige URL einer Anfrage rekonstruieren möchte, kann sie dies mit dem folgenden Algorithmus tun, beigesteuert von Ian Bicking
from urllib import quote
url = environ['wsgi.url_scheme']+'://'
if environ.get('HTTP_HOST'):
url += environ['HTTP_HOST']
else:
url += environ['SERVER_NAME']
if environ['wsgi.url_scheme'] == 'https':
if environ['SERVER_PORT'] != '443':
url += ':' + environ['SERVER_PORT']
else:
if environ['SERVER_PORT'] != '80':
url += ':' + environ['SERVER_PORT']
url += quote(environ.get('SCRIPT_NAME', ''))
url += quote(environ.get('PATH_INFO', ''))
if environ.get('QUERY_STRING'):
url += '?' + environ['QUERY_STRING']
Beachten Sie, dass eine solche rekonstruierte URL möglicherweise nicht genau dieselbe URI ist, die vom Client angefordert wurde. Server-Rewrite-Regeln können beispielsweise die ursprünglich vom Client angeforderte URL geändert haben, um sie in eine kanonische Form zu bringen.
Unterstützung älterer (vor 2.2) Python-Versionen
Einige Server, Gateways oder Anwendungen möchten möglicherweise ältere (<2.2) Versionen von Python unterstützen. Dies ist besonders wichtig, wenn Jython eine Zielplattform ist, da zum Zeitpunkt der Erstellung dieses Dokuments keine produktionsreife Version von Jython 2.2 verfügbar ist.
Für Server und Gateways ist dies relativ einfach: Server und Gateways, die auf Versionen von Python vor 2.2 abzielen, müssen sich einfach darauf beschränken, nur eine Standard-"for"-Schleife zu verwenden, um über eine von einer Anwendung zurückgegebene iterierbare Sequenz zu iterieren. Dies ist der einzige Weg, um Quellcode-Kompatibilität sowohl mit dem Iterator-Protokoll vor 2.2 (weiter unten besprochen) als auch mit dem heutigen Iterator-Protokoll (siehe PEP 234) sicherzustellen.
(Beachten Sie, dass diese Technik notwendigerweise nur für Server, Gateways oder Middleware gilt, die in Python geschrieben sind. Die Diskussion, wie Iterator-Protokolle aus anderen Sprachen korrekt verwendet werden, liegt außerhalb des Umfangs dieses PEP.)
Für Anwendungen ist die Unterstützung von Python-Versionen vor 2.2 etwas komplexer
- Sie dürfen kein Dateiobjekt zurückgeben und erwarten, dass es als iterierbare Sequenz funktioniert, da Dateien vor Python 2.2 nicht iterierbar waren. (Im Allgemeinen sollten Sie dies ohnehin nicht tun, da dies die meiste Zeit sehr schlecht abschneiden wird!) Verwenden Sie
wsgi.file_wrapperoder eine anwendungsspezifische Wrapper-Klasse für Dateien. (Siehe Optionale plattformspezifische Dateibehandlung für mehr überwsgi.file_wrapperund ein Beispiel für eine Klasse, die Sie zum Umschließen einer Datei als iterierbare Sequenz verwenden können.) - Wenn Sie eine benutzerdefinierte iterierbare Sequenz zurückgeben, muss diese das Iterator-Protokoll vor 2.2 implementieren. Das heißt, sie muss eine
__getitem__-Methode bereitstellen, die einen ganzzahligen Schlüssel akzeptiert undIndexErrorauslöst, wenn sie erschöpft ist. (Beachten Sie, dass eingebaute Sequenztypen ebenfalls akzeptabel sind, da sie dieses Protokoll ebenfalls implementieren.)
Schließlich müssen Middleware, die Python-Versionen vor 2.2 unterstützen möchte und über Anwendungsrückgabewerte iteriert oder selbst eine iterierbare Sequenz zurückgibt (oder beides), die entsprechenden Empfehlungen oben befolgen.
(Hinweis: Es versteht sich von selbst, dass zur Unterstützung von Python-Versionen vor 2.2 jeder Server, jedes Gateway, jede Anwendung oder Middleware nur Sprachmerkmale verwenden darf, die in der Zielversion verfügbar sind, 1 und 0 anstelle von True und False usw. verwendet.)
Optionale plattformspezifische Dateibehandlung
Einige Betriebsumgebungen bieten spezielle Hochleistungs-Dateitransmissionsfunktionen, wie z. B. den Unix sendfile()-Aufruf. Server und Gateways können diese Funktionalität über einen optionalen wsgi.file_wrapper-Schlüssel im environ bereitstellen. Eine Anwendung kann diesen "File Wrapper" verwenden, um eine Datei oder ein dateiähnliches Objekt in eine iterierbare Sequenz umzuwandeln, die sie dann zurückgibt, z. B.
if 'wsgi.file_wrapper' in environ:
return environ['wsgi.file_wrapper'](filelike, block_size)
else:
return iter(lambda: filelike.read(block_size), '')
Wenn der Server oder Gateway wsgi.file_wrapper bereitstellt, muss es sich um eine aufrufbare Funktion handeln, die einen erforderlichen positionellen Parameter und einen optionalen positionellen Parameter akzeptiert. Der erste Parameter ist das als Datei zu behandelnde Objekt, das gesendet werden soll, und der zweite Parameter ist ein optionaler "Vorschlag" für die Blockgröße (den der Server/Gateway nicht verwenden muss). Die aufrufbare Funktion **muss** ein iterierbares Objekt zurückgeben und darf **keine** Datenübertragung durchführen, es sei denn, der Server/Gateway empfängt das iterierbare Objekt tatsächlich als Rückgabewert von der Anwendung. (Andernfalls könnten Middleware die Antwortdaten nicht interpretieren oder überschreiben.)
Um als "Datei-ähnlich" zu gelten, muss das von der Anwendung bereitgestellte Objekt über eine read()-Methode verfügen, die ein optionales Größenargument annimmt. Es **kann** eine close()-Methode haben, und wenn ja, **muss** das von wsgi.file_wrapper zurückgegebene iterierbare Objekt eine close()-Methode haben, die die close()-Methode des ursprünglichen Datei-ähnlichen Objekts aufruft. Wenn das "Datei-ähnliche" Objekt andere Methoden oder Attribute mit Namen hat, die denen von integrierten Python-Datei-Objekten ähneln (z. B. fileno()), **kann** der wsgi.file_wrapper davon ausgehen, dass diese Methoden oder Attribute dieselben Semantiken wie die eines integrierten Datei-Objekts haben.
Die eigentliche Implementierung der plattformspezifischen Dateiverarbeitung muss **nach** der Rückgabe der Anwendung erfolgen, und der Server oder Gateway prüft, ob ein Wrapper-Objekt zurückgegeben wurde. (Auch hier ist aufgrund der Anwesenheit von Middleware, Fehlerbehandlern und Ähnlichem nicht garantiert, dass ein erstellter Wrapper tatsächlich verwendet wird.)
Abgesehen von der Behandlung von close() sollten die Semantiken der Rückgabe eines Datei-Wrappers von der Anwendung dieselben sein, als hätte die Anwendung iter(filelike.read, '') zurückgegeben. Mit anderen Worten, die Übertragung sollte an der aktuellen Position innerhalb der "Datei" zum Zeitpunkt des Beginns der Übertragung beginnen und bis zum Ende fortgesetzt werden.
Plattformspezifische Dateiübertragungs-APIs akzeptieren normalerweise keine beliebigen "Datei-ähnlichen" Objekte. Daher muss ein wsgi.file_wrapper das bereitgestellte Objekt auf Dinge wie fileno() (Unix-ähnliche Betriebssysteme) oder java.nio.FileChannel (unter Jython) untersuchen, um festzustellen, ob das Datei-ähnliche Objekt für die Verwendung mit der plattformspezifischen API, die er unterstützt, geeignet ist.
Beachten Sie, dass der wsgi.file_wrapper **immer** ein iterierbares Objekt zurückgeben **muss**, das read() und close() umschließt, auch wenn das Objekt für die Plattform-API *nicht* geeignet ist. So sind Anwendungen, die Datei-Wrapper verwenden, plattformübergreifend portierbar. Hier ist eine einfache plattformunabhängige Datei-Wrapper-Klasse, die sowohl für alte (vor 2.2) als auch für neue Pythons geeignet ist.
class FileWrapper:
def __init__(self, filelike, blksize=8192):
self.filelike = filelike
self.blksize = blksize
if hasattr(filelike, 'close'):
self.close = filelike.close
def __getitem__(self, key):
data = self.filelike.read(self.blksize)
if data:
return data
raise IndexError
und hier ist ein Ausschnitt aus einem Server/Gateway, der ihn zur Bereitstellung des Zugriffs auf eine plattformspezifische API verwendet
environ['wsgi.file_wrapper'] = FileWrapper
result = application(environ, start_response)
try:
if isinstance(result, FileWrapper):
# check if result.filelike is usable w/platform-specific
# API, and if so, use that API to transmit the result.
# If not, fall through to normal iterable handling
# loop below.
for data in result:
# etc.
finally:
if hasattr(result, 'close'):
result.close()
Fragen und Antworten
- Warum muss
environein Dictionary sein? Was ist falsch daran, eine Unterklasse zu verwenden?Der Grund für die Anforderung eines Dictionaries ist die Maximierung der Portabilität zwischen Servern. Die Alternative wäre, eine Teilmenge der Dictionary-Methoden als standardisierte und portable Schnittstelle zu definieren. In der Praxis werden die meisten Server ein Dictionary wahrscheinlich als ausreichend erachten, und daher werden Framework-Autoren damit rechnen, dass die vollständige Menge der Dictionary-Funktionen verfügbar ist, da sie häufiger als nicht vorhanden sein werden. Wenn sich jedoch ein Server dafür entscheidet, *kein* Dictionary zu verwenden, wird es trotz der "Konformität" des Servers mit der Spezifikation zu Interoperabilitätsproblemen kommen. Daher vereinfacht die obligatorische Verwendung eines Dictionaries die Spezifikation und garantiert die Interoperabilität.
Beachten Sie, dass dies Server- oder Framework-Entwickler nicht daran hindert, spezialisierte Dienste als benutzerdefinierte Variablen *innerhalb* des
environ-Dictionaries anzubieten. Dies ist der empfohlene Ansatz für die Bereitstellung solcher Mehrwertdienste. - Warum kann man
write()aufrufen *und* Strings übergeben/ein iterierbares Objekt zurückgeben? Sollten wir nicht nur eine Möglichkeit wählen?Wenn wir nur den Iterationsansatz unterstützen würden, würden aktuelle Frameworks, die die Verfügbarkeit von "Push" voraussetzen, leiden. Aber wenn wir nur das Pushen über
write()unterstützen, leidet die Serverleistung bei der Übertragung von z. B. großen Dateien (wenn ein Worker-Thread mit der Bearbeitung einer neuen Anfrage nicht beginnen kann, bis die gesamte Ausgabe gesendet wurde). Daher ermöglicht dieser Kompromiss einer Anwendungsframework, beide Ansätze nach Bedarf zu unterstützen, jedoch mit nur geringfügig mehr Aufwand für den Server-Implementierer als ein reiner Push-Ansatz erfordern würde. - Wozu dient
close()?Wenn während der Ausführung eines Anwendungsobjekts geschrieben wird, kann die Anwendung mit einem try/finally-Block sicherstellen, dass Ressourcen freigegeben werden. Wenn die Anwendung jedoch ein iterierbares Objekt zurückgibt, werden die verwendeten Ressourcen erst freigegeben, wenn das iterierbare Objekt von der Garbage Collection gesammelt wird. Das
close()-Idiom ermöglicht es einer Anwendung, kritische Ressourcen am Ende einer Anfrage freizugeben, und ist abwärtskompatibel mit der Unterstützung für try/finally in Generatoren, wie sie von PEP 325 vorgeschlagen wird. - Warum ist diese Schnittstelle so niedrigschwellig? Ich möchte Funktion X! (z. B. Cookies, Sitzungen, Persistenz, ...)
Dies ist kein weiteres Python-Web-Framework. Es ist nur eine Möglichkeit für Frameworks, mit Webservern zu kommunizieren und umgekehrt. Wenn Sie diese Funktionen wünschen, müssen Sie ein Web-Framework wählen, das die gewünschten Funktionen bietet. Und wenn dieses Framework es Ihnen ermöglicht, eine WSGI-Anwendung zu erstellen, sollten Sie sie auf den meisten WSGI-kompatiblen Servern ausführen können. Einige WSGI-Server bieten möglicherweise auch zusätzliche Dienste über Objekte an, die in ihrem
environ-Dictionary bereitgestellt werden. Weitere Informationen finden Sie in der Dokumentation des entsprechenden Servers. (Natürlich sind Anwendungen, die solche Erweiterungen verwenden, nicht auf andere WSGI-basierte Server portierbar.) - Warum CGI-Variablen anstelle von guter alter HTTP-Header verwenden? Und warum sie mit WSGI-definierten Variablen mischen?
Viele bestehende Web-Frameworks basieren stark auf der CGI-Spezifikation, und bestehende Webserver wissen, wie CGI-Variablen generiert werden. Im Gegensatz dazu sind alternative Wege zur Darstellung eingehender HTTP-Informationen fragmentiert und haben wenig Marktanteil. Daher scheint die Verwendung des CGI-"Standards" ein guter Weg zu sein, um bestehende Implementierungen zu nutzen. Was die Vermischung mit WSGI-Variablen betrifft, würde eine Trennung nur dazu führen, dass zwei Dictionary-Argumente übergeben werden müssten, während dies keine wirklichen Vorteile bringen würde.
- Was ist mit dem Status-String? Können wir nicht einfach die Zahl verwenden und
200statt"200 OK"übergeben?Dies würde den Server oder Gateway komplizieren, da sie eine Tabelle mit numerischen Statuscodes und entsprechenden Nachrichten benötigen würden. Im Gegensatz dazu ist es für einen Anwendungs- oder Framework-Autor einfach, den zusätzlichen Text für den spezifischen Antwortcode einzugeben, den sie verwenden, und bestehende Frameworks haben oft bereits eine Tabelle mit den benötigten Nachrichten. Insgesamt scheint es besser zu sein, die Verantwortung der Anwendung/des Frameworks zu überlassen und nicht dem Server oder Gateway.
- Warum wird garantiert, dass
wsgi.run_oncedie App nicht nur einmal ausführt?Da es sich lediglich um einen Vorschlag an die Anwendung handelt, dass sie sich für "seltene Ausführungen" vorbereiten soll. Dies ist für Anwendungsframeworks gedacht, die mehrere Betriebsmodi für Caching, Sitzungen usw. haben. Im Modus "mehrfache Ausführung" können solche Frameworks Caches vorladen und z. B. Protokolle oder Sitzungsdaten nach jeder Anfrage nicht auf die Festplatte schreiben. Im Modus "einmalige Ausführung" vermeiden solche Frameworks das Vorladen und leeren nach jeder Anfrage alle notwendigen Schreibvorgänge.
Um jedoch eine Anwendung oder ein Framework zu testen und die korrekte Funktionsweise im letzteren Modus zu überprüfen, kann es notwendig (oder zumindest zweckmäßig) sein, es mehr als einmal aufzurufen. Daher sollte eine Anwendung nicht davon ausgehen, dass sie definitiv nicht erneut ausgeführt wird, nur weil sie mit
wsgi.run_onceaufTruegesetzt aufgerufen wird. - Funktion X (Dictionaries, Callables etc.) sind für die Verwendung in Anwendungscode hässlich; warum verwenden wir keine Objekte stattdessen?
Alle diese Implementierungsentscheidungen von WSGI sollen die Funktionen bewusst voneinander entkoppeln. Die Neukombination dieser Funktionen in gekapselten Objekten erschwert das Schreiben von Servern oder Gateways und erschwert das Schreiben von Middleware, die nur kleine Teile der Gesamtfunktionalität ersetzt oder modifiziert, um ein Vielfaches.
Im Wesentlichen möchte Middleware ein "Chain of Responsibility"-Muster haben, bei dem sie als "Handler" für einige Funktionen fungieren kann, während andere unverändert bleiben. Dies ist mit normalen Python-Objekten schwierig zu erreichen, wenn die Schnittstelle erweiterbar bleiben soll. Beispielsweise muss man
__getattr__oder__getattribute__-Überschreibungen verwenden, um sicherzustellen, dass Erweiterungen (wie Attribute, die von zukünftigen WSGI-Versionen definiert werden) durchgeleitet werden.Diese Art von Code ist bekanntermaßen schwer zu 100 % korrekt zu bekommen, und nur wenige Leute werden ihn selbst schreiben wollen. Sie werden daher die Implementierungen anderer Leute kopieren, aber diese nicht aktualisieren, wenn die Person, von der sie kopiert haben, einen weiteren Eckfall korrigiert hat.
Darüber hinaus wären diese notwendigen Boilerplates reine zusätzliche Belastung, eine Entwicklersteuer, die von Middleware-Entwicklern gezahlt wird, um eine etwas schönere API für Anwendungs-Framework-Entwickler zu unterstützen. Aber Anwendungs-Framework-Entwickler werden normalerweise nur *ein* Framework aktualisieren, um WSGI zu unterstützen, und zwar in einem sehr begrenzten Teil ihres Frameworks insgesamt. Es wird wahrscheinlich ihre erste (und vielleicht ihre einzige) WSGI-Implementierung sein, und daher werden sie wahrscheinlich mit dieser Spezifikation zur Hand implementieren. Daher wäre der Aufwand, die API mit Objektattributen und Ähnlichem "schöner" zu machen, für dieses Publikum wahrscheinlich verschwendet.
Wir ermutigen diejenigen, die eine schönere (oder anderweitig verbesserte) WSGI-Schnittstelle für die Verwendung in direkter Webanwendungsentwicklung (im Gegensatz zur Web-Framework-Entwicklung) wünschen, APIs oder Frameworks zu entwickeln, die WSGI für die bequeme Nutzung durch Anwendungsentwickler umschließen. Auf diese Weise kann WSGI für Server- und Middleware-Autoren praktisch niedrigschwellig bleiben, ohne für Anwendungsentwickler "hässlich" zu sein.
Vorgeschlagen/Diskutiert
Diese Punkte werden derzeit im Web-SIG und anderswo diskutiert oder stehen auf der "To-Do"-Liste des PEP-Autors.
- Sollte
wsgi.inputein Iterator statt einer Datei sein? Dies würde für asynchrone Anwendungen und gestückelte Eingangsströme helfen. - Optionale Erweiterungen werden diskutiert, um die Iteration der Ausgabe einer Anwendung zu pausieren, bis Eingaben verfügbar sind oder bis ein Callback erfolgt.
- Fügen Sie einen Abschnitt über synchrone vs. asynchrone Apps und Server, die relevanten Threading-Modelle und Probleme/Designziele in diesen Bereichen hinzu.
Danksagungen
Vielen Dank an die vielen Leute auf der Web-SIG-Mailingliste, deren nachdenkliches Feedback diesen überarbeiteten Entwurf ermöglicht hat. Insbesondere
- Gregory "Grisha" Trubetskoy, Autor von
mod_python, der den ersten Entwurf als nicht vorteilhafter als "plain old CGI" kritisierte und mich dadurch ermutigte, einen besseren Ansatz zu suchen. - Ian Bicking, der mir half, die Multithreading- und Multiprocessing-Optionen korrekt zu spezifizieren, und mich auch dazu drängte, einen Mechanismus bereitzustellen, mit dem Server benutzerdefinierte Erweiterungsdaten an eine Anwendung liefern können.
- Tony Lownds, der auf die Idee einer
start_response-Funktion kam, die Status und Header entgegennahm und einewrite-Funktion zurückgab. Seine Eingaben prägten auch das Design der Ausnahmebehandlungsfunktionen, insbesondere im Hinblick auf die Ermöglichung von Middleware, die Anwendungsfehlermeldungen überschreibt. - Alan Kennedy, dessen mutige Versuche, WSGI unter Jython zu implementieren (lange bevor die Spezifikation fertig war), dazu beitrugen, den Abschnitt "Unterstützung älterer Python-Versionen" sowie die optionale
wsgi.file_wrapper-Funktion zu gestalten. - Mark Nottingham, der die Spezifikation ausgiebig auf Probleme mit der Konformität mit HTTP-RFCs überprüfte, insbesondere in Bezug auf HTTP/1.1-Funktionen, von denen ich bis zu seinen Hinweisen nicht einmal wusste, dass sie existieren.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0333.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT