1993. aastal oli veeb veel lapsekingades, umbes 14 miljonit kasutajat ja 100 veebisaiti . Lehed olid staatilised, kuid juba oli vaja luua dünaamilist sisu, näiteks ajakohaseid uudiseid ja andmeid. Sellele reageerides rakendasid Rob McCool ja teised kaastöötajad riikliku superarvutirakenduste keskuses (NCSA) Common Gateway Interface'i (CGI). HTTPd veebiserver (Apache'i eelkäija). See oli esimene veebiserver, mis suutis teenida eraldi rakenduse loodud sisu.
Sellest ajast alates on Interneti-kasutajate arv plahvatuslikult kasvanud ja dünaamilised veebisaidid on muutunud üldlevinudiks. Esmakordselt uue keele õppimisel või isegi kodeerimise õppimisel tahavad arendajad piisavalt kiiresti teada, kuidas oma kood veebi siduda.
Pärast CGI loomist on palju muutunud. CGI-lähenemine muutus ebapraktiliseks, kuna see nõudis iga taotluse korral uue protsessi loomist, raisates mälu ja protsessorit. Ilmusid veel mõned madalama taseme lähenemisviisid, nagu FastCGI] (http://www.fastcgi.com/) (1996) ja mod_python (2000), pakkudes erinevaid liideseid Pythoni veebiraamistike ja veebiserveri vahel. Erinevate lähenemisviiside vohamise tõttu piiras arendaja raamistiku valik lõpuks veebiserverite valikuid ja vastupidi.
Selle probleemi lahendamiseks tegi 2003. aastal ettepaneku Phillip J. Eby PEP-0333 , Pythoni veebiserveri lüüsi liides (WSGI). Idee oli pakkuda kõrgetasemelist universaalset liidest Pythoni rakenduste ja veebiserverite vahel.
2003. aastal PEP-3333 värskendas WSGI-liidest Python 3 toe lisamiseks. Tänapäeval kasutavad peaaegu kõik Pythoni raamistikud WSGI-d kui (kui mitte ainsat) vahendit oma veebiserveritega suhtlemiseks. See on nii Django , Kolb ja paljud teised populaarsed raamistikud teevad seda.
Selle artikli eesmärk on anda lugejale pilk WSGI toimimisele ja võimaldada lugejal ehitada lihtne WSGI-rakendus või server. See ei ole siiski ammendav ja arendajad, kes kavatsevad juurutada tootmiseks valmis servereid või rakendusi, peaksid põhjalikumalt uurima WSGI spetsifikatsioon .
WSGI määratleb lihtsad reeglid, millele server ja rakendus peavad vastama. Alustame selle üldise mustri ülevaatamisest.
milleks node js-i kasutatakse
Python 3.5-s on rakenduse liidesed järgmised:
def application(environ, start_response): body = b'Hello world!
' status = '200 OK' headers = [('Content-type', 'text/plain')] start_response(status, headers) return [body]
Python 2.7-s ei oleks see liides palju erinev; ainus muudatus oleks see, et keha esindab tähis str
objekti asemel bytes
üks.
Kuigi oleme antud juhul funktsiooni kasutanud, on ükskõik milline helistatav teeb. Siinsed rakenduse objekti reeglid on järgmised.
environ
ja start_response
parameetrid. start_response
enne keha saatmist tagasihelistamine. Teine näide objektist, mis vastab nendele reeglitele ja tooks sama efekti, on järgmine:
class Application: def __init__(self, environ, start_response): self.environ = environ self.start_response = start_response def __iter__(self): body = b'Hello world!
' status = '200 OK' headers = [('Content-type', 'text/plain')] self.start_response(status, headers) yield body
WSGI-server võib selle rakendusega liidestuda niimoodi:
def write(chunk): '''Write data back to client''' ... def send_status(status): '''Send HTTP status code''' ... def send_headers(headers): '''Send HTTP headers''' ... def start_response(status, headers): '''WSGI start_response callable''' send_status(status) send_headers(headers) return write # Make request to application response = application(environ, start_response) try: for chunk in response: write(chunk) finally: if hasattr(response, 'close'): response.close()
Nagu võisite tähele panna, start_response
helistatav tagastas a write
helistatav, mida rakendus võib kasutada andmete kliendile tagasi saatmiseks, kuid seda ei kasutatud meie rakenduse koodi näites. See write
liides on aegunud ja me võime seda praegu eirata. Sellest räägitakse põgusalt artiklis hiljem.
Serveri kohustuste teine eripära on helistada valikulisele close
meetod iteraatoril, kui see on olemas. Nagu Graham Dumpletoni artiklis märgitud siin , on see WSGI sageli tähelepanuta jäetud funktsioon. Selle meetodi kutsumine kui see on olemas , lubab rakendusel vabastada ressursse, mis tal veel võivad olla.
environ
Argumentenviron
parameeter peaks olema sõnaraamatu objekt. Seda kasutatakse taotluste ja serveriteabe edastamiseks rakendusse, täpselt samamoodi nagu CGI. Tegelikult kehtivad WSGI-s kõik CGI-keskkonnamuutujad ja server peaks edastama kõik rakenduse kohta kehtivad.
Kuigi on palju valikulisi võtmeid, mida saab edasi anda, on mitu neist kohustuslikud. Võttes näiteks järgmise GET
taotlus:
$ curl 'http://localhost:8000/auth?user=obiwan&token=123'
Need on võtmed, mida server kasutab peab pakutavad väärtused:
Võti | Väärtus | Kommentaarid |
---|---|---|
REQUEST_METHOD | 'GET' | |
SCRIPT_NAME | '' | serveri seadistus sõltub |
PATH_INFO | '/auth' | |
QUERY_STRING | 'token=123' | |
CONTENT_TYPE | '' | |
CONTENT_LENGTH | '' | |
SERVER_NAME | '127.0.0.1' | serveri seadistus sõltub |
SERVER_PORT | '8000' | |
SERVER_PROTOCOL | 'HTTP/1.1' | |
HTTP_(...) | Kliendi pakutavad HTTP-päised | |
wsgi.version | (1, 0) | kahekordne WSGI versiooniga |
wsgi.url_scheme | 'http' | |
wsgi.input | Failitaoline objekt | |
wsgi.errors | Failitaoline objekt | |
wsgi.multithread | False | True kui server on mitmekeermeline |
wsgi.multiprocess | False | True kui server töötab mitu protsessi |
wsgi.run_once | False | True kui server eeldab, et see skript töötab ainult üks kord (nt CGI-keskkonnas) |
Selle reegli erand on see, et kui üks neist võtmetest peaks olema tühi (nagu | ülaltoodud tabelis CONTENT_TYPE
), siis saab need sõnastikust välja jätta ja eeldatakse, et need vastavad tühjale stringile.
wsgi.input
ja wsgi.errors
Enamik environ
võtmed on otsekohesed, kuid kaks neist väärivad veidi täpsustust: wsgi.input
, mis peab sisaldama voogu koos kliendi päringukehaga ja wsgi.errors
, kus rakendus teatab kõigist ilmnenud vigadest. Rakendusest wsgi.errors
saadetud vead tavaliselt saadetakse serveri tõrglogi.
Need kaks võtit peavad sisaldama failitaolisi objekte; see tähendab objektid, mis pakuvad liideseid voogudeks lugemiseks või kirjutamiseks, täpselt nagu objekt, mille saame Pythonis faili või sokli avamisel. See võib esialgu tunduda keeruline, kuid õnneks annab Python meile selle käsitsemiseks häid tööriistu.
Esiteks, millistest voogudest me räägime? Vastavalt WSGI määratlusele wsgi.input
ja wsgi.errors
peab käsitsema bytes
objektid Python 3-s ja str
objektid Python 2-s. Mõlemal juhul, kui soovime kasutada mälus olevat puhvrit andmete edastamiseks või hankimiseks WSGI-liidese kaudu, võime kasutada klassi io.BytesIO
Näiteks kui kirjutame WSGI-serverit, võiksime taotlusekeha rakendusele pakkuda järgmiselt:
import io ... request_data = 'some request body' environ['wsgi.input'] = io.BytesIO(request_data)
import io ... request_data = 'some request body'.encode('utf-8') # bytes object environ['wsgi.input'] = io.BytesIO(request_data)
Rakenduse poolel, kui me sooviksime saadud voo sisendi stringiks muuta, tahaksime kirjutada midagi sellist:
readstr = environ['wsgi.input'].read() # returns str object
readbytes = environ['wsgi.input'].read() # returns bytes object readstr = readbytes.decode('utf-8') # returns str object
wsgi.errors
voogu tuleks kasutada rakenduse tõrgetest serverisse teatamiseks ja read tuleks lõpetada tähega
Veebiserver peaks hoolitsema selle eest, et süsteem teisendataks teisele reale.
start_response
Argumentstart_response
argument peab olema kutsutav kahe nõutava argumendiga, nimelt status
ja headers
ning üks valikuline argument exc_info
. Enne kui mõni kehaosa veebiserverisse tagasi saadetakse, peab rakendus sellele helistama.
Esimeses rakenduse näites selle artikli alguses oleme vastuse põhiosa tagastanud loendina ja seega pole meil võimalik kontrollida, millal loend uuesti tehakse. Seetõttu pidime helistama start_response
enne loendi tagastamist.
Teises oleme helistanud start_response
vahetult enne reaktsioonikeha esimese (ja antud juhul ainsa) tüki saamist. Mõlemad viisid kehtivad WSGI spetsifikatsioonides.
Veebiserveri poolelt helistatakse start_response
ei peaks tegelikult päiseid kliendile saatma, vaid lükake seda edasi, kuni vastusekehas on vähemalt üks tühi bytesting, mis kliendile tagasi saadetakse. See arhitektuur võimaldab vigadest õigesti teatada kuni rakenduse viimase võimaliku hetkeni.
status
Argument start_response
status
argument edastati start_response
-le tagasihelistamine peab olema string, mis koosneb HTTP olekukoodist ja kirjeldusest, eraldatuna ühe tühikuga. Kehtivad näited on: '200 OK'
või '404 Not Found'
.
headers
Argument start_response
headers
argument edastati start_response
-le tagasihelistamine peab olema Python list
/ _ + _ | s, kusjuures iga tupel on moodustatud tuple
. Nii päise nimi kui ka väärtus peavad olema stringid (olenemata Pythoni versioonist). See on haruldane näide, kus tüüp on oluline, kuna seda nõuab WSGI spetsifikatsioon.
Siin on sobiv näide selle kohta, mida (header_name, header_value)
argument võib välja näha:
milleks Adobe kogemuste disaini kasutatakse
header
HTTP-päised ei erista suurtähti ja kui me kirjutame WSGI-ga ühilduvat veebiserverit, on see, mida nende päiste kontrollimisel arvestada. Samuti ei peaks rakenduse esitatud päiste loend olema täielik. Enne vastuse kliendile saatmist on serveri kohustus tagada, et kõik vajalikud HTTP-päised oleksid olemas, täites kõik päised, mida rakendus ei paku.
response_body = json.dumps(data).encode('utf-8') headers = [('Content-Type', 'application/json'), ('Content-Length', str(len(response_body))]
Argument exc_info
start_response
tagasihelistamine peaks toetama kolmandat argumenti start_response
, mida kasutatakse tõrgete käsitlemisel. Selle argumendi õige kasutamine ja rakendamine on tootmise veebiserverite ja -rakenduste jaoks ülioluline, kuid jääb selle artikli reguleerimisalast välja.
Lisateavet selle kohta leiate WSGI spetsifikatsioonist, siin .
exc_info
Tagastusväärtus - start_response
Helista tagasiTagasiühilduvuse tagamiseks peaksid WSGI-d rakendavad veebiserverid tagastama write
helistatav. See tagasihelistamine peaks võimaldama rakendusel kirjutada kehavastuse andmed otse kliendile tagasi, selle asemel, et need iteraatori kaudu serverile edastada.
Hoolimata oma olemasolust, on see liides ära ja uued rakendused peaksid selle kasutamisest hoiduma.
WSGI-d rakendavad rakendused peaksid reageeriva keha genereerima, tagastades korduva objekti. Enamiku rakenduste puhul pole vastuse sisu eriti suur ja mahub kergesti serveri mällu. Sel juhul on kõige tõhusam viis selle saatmiseks korraga, ühe elemendiga iterable. Erijuhtudel, kui kogu keha mällu laadimine on teostamatu, võib rakendus selle korduva liidese kaudu selle osaliselt tagastada.
Siin on Python 2 ja Python 3 WSGI vahel vaid väike erinevus: Python 3-s esindab vastuskeha write
esemed; Python 2-s on selle õige tüüp bytes
.
UTF-8 stringide teisendamine str
-ks või bytes
on lihtne ülesanne:
str
body = 'unicode stuff'.encode('utf-8')
Kui soovite rohkem teada saada Python 2 unicode'i ja bytestringi käitlemise kohta, on siin kena õpetus Youtube .
WSGI-d rakendavad veebiserverid peaksid toetama ka body = u'unicode stuff'.encode('utf-8')
tagasihelistamine tagurpidi ühilduvuse tagamiseks, nagu eespool kirjeldatud.
Selle lihtsa liidese mõistmisega saame hõlpsasti luua skripte oma rakenduste testimiseks, ilma et peaksime tegelikult serverit käivitama.
Võtke näiteks see väike skript:
write
Sel moel võime näiteks initsialiseerida mõned testiandmed ja mõnitada moodulid oma rakendusse ning teha from io import BytesIO def get(app, path = '/', query = ''): response_status = [] response_headers = [] def start_response(status, headers): status = status.split(' ', 1) response_status.append((int(status[0]), status[1])) response_headers.append(dict(headers)) environ = { 'HTTP_ACCEPT': '*/*', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_USER_AGENT': 'TestAgent/1.0', 'PATH_INFO': path, 'QUERY_STRING': query, 'REQUEST_METHOD': 'GET', 'SERVER_NAME': '127.0.0.1', 'SERVER_PORT': '8000', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'TestServer/1.0', 'wsgi.errors': BytesIO(b''), 'wsgi.input': BytesIO(b''), 'wsgi.multiprocess': False, 'wsgi.multithread': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.version': (1, 0), } response_body = app(environ, start_response) merged_body = ''.join((x.decode('utf-8') for x in response_body)) if hasattr(response_body, 'close'): response_body.close() return {'status': response_status[0], 'headers': response_headers[0], 'body': merged_body}
helistab, et testida, kas see vastab sellele. Näeme, et see pole tegelik veebiserver, vaid liidestub meie rakendusega võrreldaval viisil, pakkudes rakendusele GET
tagasihelistamine ja sõnastik meie keskkonnamuutujatega. Taotluse lõpus kulutab ta vastuse keha iteraatori ja tagastab stringi kogu sisuga. Sarnaseid meetodeid (või üldisi) saab luua erinevat tüüpi HTTP-päringute jaoks.
Selles artiklis ei ole me käsitlenud seda, kuidas WSGI failide üleslaadimisega tegeleb, kuna seda võiks pidada 'täpsemaks' funktsiooniks, mis ei sobi sissejuhatava artikli jaoks. Kui soovite selle kohta rohkem teada saada, vaadake Failide käitlemist käsitlev jaotis PEP-3333 .
Loodan, et see artikkel on kasulik, et aidata paremini mõista, kuidas Python veebiserveritega suhtleb, ja võimaldab arendajatel seda liidest huvitaval ja loomingulisel viisil kasutada.
Tahaksin tänada oma toimetajat Nick McCrea selle artikli aitamise eest. Tema töö tõttu sai algtekst palju selgemaks ja mitmed vead ei jäänud parandamata.