Išsamus vadovas pasauliniams kūrėjams apie Python http.server (anksčiau BaseHTTPServer) pritaikymą kuriant paprastas API, dinaminius žiniatinklio serverius ir galingus vidinius įrankius.
Python integruoto HTTP serverio valdymas: išsamus tinkinimo vadovas
Python yra garsus savo "viskas įskaičiuota" filosofija, suteikiantis turtingą standartinę biblioteką, kuri leidžia kūrėjams kurti funkcines programas su minimaliomis išorinėmis priklausomybėmis. Vienas iš naudingiausių, bet dažnai nepastebimų, yra integruotas HTTP serveris. Nesvarbu, ar jį žinote pagal jo modernų Python 3 pavadinimą, http.server
, ar pagal senesnį Python 2 pavadinimą, BaseHTTPServer
, šis modulis yra vartai į žiniatinklio protokolų supratimą ir lengvų žiniatinklio paslaugų kūrimą.
Nors daugelis kūrėjų pirmą kartą su juo susiduria kaip su vienos eilutės komanda failams aptarnauti kataloge, tikroji jo galia slypi jo išplečiamume. Apibrėžę pagrindinius jo komponentus, galite paversti šį paprastą failų serverį į individualizuotą žiniatinklio programą, fiktyvų API, skirtą priekinės dalies kūrimui, duomenų priėmėją IoT įrenginiams arba galingą vidinį įrankį. Šis vadovas nuves jus nuo pagrindų iki pažangaus tinkinimo, suteikdamas jums galimybę panaudoti šį fantastišką modulį savo projektams.
Pagrindai: Paprastas serveris iš komandinės eilutės
Prieš pasinerdami į kodą, pažvelkime į dažniausiai pasitaikantį naudojimo atvejį. Jei turite įdiegtą Python, jūs jau turite žiniatinklio serverį. Naršykite bet kurį kompiuterio katalogą naudodami terminalą arba komandinę eilutę ir paleiskite šią komandą (Python 3):
python -m http.server 8000
Akimirksniu turite žiniatinklio serverį, veikiantį 8000 prievadu, aptarnaujantį dabartinės vietos failus ir subkatalogus. Galite pasiekti jį iš savo naršyklės adresu http://localhost:8000
. Tai neįtikėtinai naudinga:
- Greitai dalintis failais per vietinį tinklą.
- Testuoti paprastus HTML, CSS ir JavaScript projektus be sudėtingo sąrankos.
- Tikrinti, kaip žiniatinklio serveris apdoroja skirtingus užklausimus.
Tačiau ši viena eilutė tėra ledkalnio viršūnė. Ji paleidžia iš anksto sukurtą, bendrą serverį. Norėdami pridėti pasirinktinę logiką, apdoroti skirtingus užklausų tipus arba generuoti dinaminį turinį, turime parašyti savo Python scenarijų.
Pagrindinių komponentų supratimas
Žiniatinklio serveris, sukurtas naudojant šį modulį, susideda iš dviejų pagrindinių dalių: serverio ir tvarkyklės. Suprasti jų skirtingus vaidmenis yra raktas į veiksmingą tinkinimą.
1. Serveris: HTTPServer
Serverio užduotis yra klausytis įeinančių tinklo jungčių konkrečiu adresu ir prievadu. Tai variklis, kuris priima TCP jungtis ir perduoda jas tvarkyklei apdoroti. http.server
modulyje tai paprastai tvarkoma naudojant HTTPServer
klasę. Jūs sukuriate jo egzempliorių pateikdami serverio adresą (tokį kaip ('localhost', 8000)
) ir tvarkyklės klasę.
Pagrindinė jo atsakomybė yra valdyti tinklo lizdą ir organizuoti užklausos-atsakymo ciklą. Daugeliui tinkinimų jums nereikės keisti pačios HTTPServer
klasės, tačiau svarbu žinoti, kad ji ten yra ir valdo procesą.
2. Tvarkyklė: BaseHTTPRequestHandler
Čia vyksta magija. Tvarkyklė yra atsakinga už įeinančios HTTP užklausos analizavimą, supratimą, ko klientas prašo, ir atitinkamo HTTP atsakymo generavimą. Kiekvieną kartą, kai serveris gauna naują užklausą, jis sukuria jūsų tvarkyklės klasės egzempliorių jai apdoroti.
http.server
modulis pateikia keletą iš anksto sukurtų tvarkyklių:
BaseHTTPRequestHandler
: Tai pats pagrindinis tvarkyklė. Ji analizuoja užklausą ir antraštes, bet nežino, kaip atsakyti į konkrečius užklausos metodus, tokius kaip GET arba POST. Tai puiki bazinė klasė, iš kurios galima paveldėti, kai norite viską kurti nuo nulio.SimpleHTTPRequestHandler
: Tai paveldi išBaseHTTPRequestHandler
ir prideda logiką failams aptarnauti iš dabartinio katalogo. Kai paleidžiatepython -m http.server
, naudojate šią tvarkyklę. Tai puikus atspirties taškas, jei norite pridėti pasirinktinę logiką prie numatytojo failų aptarnavimo elgesio.CGIHTTPRequestHandler
: Tai išplečiaSimpleHTTPRequestHandler
, kad apdorotų ir CGI scenarijus. Tai mažiau paplitę šiuolaikiniame žiniatinklio kūrime, bet yra dalis bibliotekos istorijos.
Beveik visoms pasirinktinėms serverio užduotims jūsų darbas apims naujos klasės, kuri paveldi iš BaseHTTPRequestHandler
arba SimpleHTTPRequestHandler
, kūrimą ir jos metodų perrašymą.
Jūsų pirmasis pasirinktinis serveris: "Sveikas, pasauli!" pavyzdys
Pereikime už komandinės eilutės ir parašykime paprastą Python scenarijų serveriui, kuris atsako pasirinktiniu pranešimu. Paveldėsime iš BaseHTTPRequestHandler
ir įgyvendinsime do_GET
metodą, kuris automatiškai iškviečiamas apdoroti bet kokias HTTP GET užklausas.
Sukurkite failą pavadinimu custom_server.py
:
# Use http.server for Python 3
from http.server import BaseHTTPRequestHandler, HTTPServer
import time
hostName = "localhost"
serverPort = 8080
class MyServer(BaseHTTPRequestHandler):
def do_GET(self):
# 1. Send the response status code
self.send_response(200)
# 2. Send headers
self.send_header("Content-type", "text/html")
self.end_headers()
# 3. Write the response body
self.wfile.write(bytes("<html><head><title>My Custom Server</title></head>", "utf-8"))
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("<body>", "utf-8"))
self.wfile.write(bytes("<p>This is a custom server, created with Python's http.server.</p>", "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print(f"Server started http://{hostName}:{serverPort}")
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
Norėdami tai paleisti, terminale įvykdykite python custom_server.py
. Kai apsilankysite http://localhost:8080
savo naršyklėje, pamatysite savo pasirinktinį HTML pranešimą. Jei apsilankysite kitu keliu, pvz., http://localhost:8080/some/path
, pranešimas atspindės tą kelią.
Išskaidykime do_GET
metodą:
self.send_response(200)
: Tai siunčia HTTP būsenos eilutę.200 OK
yra standartinis atsakymas į sėkmingą užklausą.self.send_header("Content-type", "text/html")
: Tai siunčia HTTP antraštę. Čia mes pasakome naršyklei, kad turinys, kurį siunčiame, yra HTML. Tai labai svarbu, kad naršyklė tinkamai atvaizduotų puslapį.self.end_headers()
: Tai siunčia tuščią eilutę, signalizuojančią HTTP antraščių pabaigą ir atsakymo turinio pradžią.self.wfile.write(...)
:self.wfile
yra į failą panašus objektas, į kurį galite rašyti savo atsakymo turinį. Jis tikisi baitų, o ne eilučių, todėl turime užkoduoti savo HTML eilutę į baitus naudodamibytes("...")
.
Pažangus tinkinimas: praktiniai receptai
Dabar, kai suprantate pagrindus, panagrinėkime galingesnius tinkinimus.
POST užklausų apdorojimas (do_POST
)
Žiniatinklio programoms dažnai reikia gauti duomenis, pavyzdžiui, iš HTML formos arba API iškvietimo. Tai paprastai daroma naudojant POST užklausą. Norėdami tai apdoroti, turite perrašyti do_POST
metodą.
do_POST
viduje turite perskaityti užklausos turinį. Šio turinio ilgis nurodytas Content-Length
antraštėje.
Štai tvarkyklės, kuri skaito JSON duomenis iš POST užklausos ir atspindi juos atgal, pavyzdys:
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
class APIServer(BaseHTTPRequestHandler):
def _send_cors_headers(self):
"""Sends headers to allow cross-origin requests"""
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
def do_OPTIONS(self):
"""Handles pre-flight CORS requests"""
self.send_response(200)
self._send_cors_headers()
self.end_headers()
def do_POST(self):
# 1. Read the content-length header
content_length = int(self.headers['Content-Length'])
# 2. Read the request body
post_data = self.rfile.read(content_length)
# For demonstration, let's log the received data
print(f"Received POST data: {post_data.decode('utf-8')}")
# 3. Process the data (here, we just echo it back as JSON)
try:
received_json = json.loads(post_data)
response_data = {"status": "success", "received_data": received_json}
except json.JSONDecodeError:
self.send_response(400) # Bad Request
self.end_headers()
self.wfile.write(bytes('{"error": "Invalid JSON"}', "utf-8"))
return
# 4. Send a response
self.send_response(200)
self._send_cors_headers()
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(response_data).encode("utf-8"))
# Main execution block remains the same...
if __name__ == "__main__":
# ... (use the same HTTPServer setup as before, but with APIServer as the handler)
server_address = ('localhost', 8080)
httpd = HTTPServer(server_address, APIServer)
print('Starting server on port 8080...')
httpd.serve_forever()
Pastaba dėl CORS: do_OPTIONS
metodas ir _send_cors_headers
funkcija yra įtraukti, kad apdorotų Cross-Origin Resource Sharing (CORS). Tai dažnai būtina, jei iškviečiate savo API iš žiniatinklio puslapio, aptarnaujamo iš skirtingos kilmės (domeno/prievado).
Paprasto API kūrimas su JSON atsakymais
Išplėskime ankstesnį pavyzdį, kad sukurtume serverį su pagrindiniu maršrutizavimu. Galime patikrinti self.path
atributą, kad nustatytume, kokio ištekliaus klientas prašo, ir atitinkamai atsakyti. Tai leidžia mums sukurti kelis API galinius taškus viename serveryje.
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
# Mock data
users = {
1: {"name": "Alice", "country": "Canada"},
2: {"name": "Bob", "country": "Australia"}
}
class APIHandler(BaseHTTPRequestHandler):
def _set_headers(self, status_code=200):
self.send_response(status_code)
self.send_header("Content-type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
def do_GET(self):
parsed_path = urlparse(self.path)
path = parsed_path.path
if path == "/api/users":
self._set_headers()
self.wfile.write(json.dumps(list(users.values())).encode("utf-8"))
elif path.startswith("/api/users/"):
try:
user_id = int(path.split('/')[-1])
user = users.get(user_id)
if user:
self._set_headers()
self.wfile.write(json.dumps(user).encode("utf-8"))
else:
self._set_headers(404)
self.wfile.write(json.dumps({"error": "User not found"}).encode("utf-8"))
except ValueError:
self._set_headers(400)
self.wfile.write(json.dumps({"error": "Invalid user ID"}).encode("utf-8"))
else:
self._set_headers(404)
self.wfile.write(json.dumps({"error": "Not Found"}).encode("utf-8"))
# Main execution block as before, using APIHandler
# ...
Su šia tvarkykle jūsų serveris dabar turi primityvią maršrutizavimo sistemą:
- GET užklausa į
/api/users
grąžins visų vartotojų sąrašą. - GET užklausa į
/api/users/1
grąžins Alisos išsamią informaciją. - Bet kuris kitas kelias sukels 404 klaidos neradimą.
Failų ir dinaminio turinio aptarnavimas kartu
Ką daryti, jei norite turėti dinaminį API, bet taip pat aptarnauti statinius failus (pvz., index.html
) iš to paties serverio? Lengviausias būdas yra paveldėti iš SimpleHTTPRequestHandler
ir deleguoti numatytąjį jo elgesį, kai užklausa neatitinka jūsų pasirinktinių kelių.
Funkcija super()
yra jūsų geriausias draugas čia. Ji leidžia jums iškviesti tėvinės klasės metodą.
import json
from http.server import SimpleHTTPRequestHandler, HTTPServer
class HybridHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/api/status':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {'status': 'ok', 'message': 'Server is running'}
self.wfile.write(json.dumps(response).encode('utf-8'))
else:
# For any other path, fall back to the default file-serving behavior
super().do_GET()
# Main execution block as before, using HybridHandler
# ...
Dabar, jei sukursite index.html
failą tame pačiame kataloge ir paleisite šį scenarijų, apsilankę http://localhost:8080/
bus aptarnaujamas jūsų HTML failas, o apsilankę http://localhost:8080/api/status
grąžins jūsų pasirinktinį JSON atsakymą.
Pastaba apie Python 2 (BaseHTTPServer
)
Nors Python 2 nebepalaikomas, galite susidurti su senstelėjusiu kodu, kuris naudoja jo HTTP serverio versiją. Koncepcijos yra identiškos, bet modulio pavadinimai yra skirtingi. Štai greitas vertimo vadovas:
- Python 3:
http.server
-> Python 2:BaseHTTPServer
,SimpleHTTPServer
- Python 3:
socketserver
-> Python 2:SocketServer
- Python 3:
from http.server import BaseHTTPRequestHandler
-> Python 2:from BaseHTTPServer import BaseHTTPRequestHandler
Metodų pavadinimai (do_GET
, do_POST
) ir pagrindinė logika išlieka tie patys, todėl gana paprasta perkelti senus scenarijus į Python 3.
Gamybos aspektai: kada pereiti toliau
Python integruotas HTTP serveris yra fenomenalus įrankis, bet jis turi savo apribojimų. Svarbu suprasti, kada tai yra tinkamas pasirinkimas ir kada turėtumėte kreiptis į patikimesnį sprendimą.
1. Lygiagretumas ir našumas
Pagal numatytuosius nustatymus HTTPServer
yra vieno sriegio ir apdoroja užklausas nuosekliai. Jei vienos užklausos apdorojimas užtrunka ilgai, ji blokuos visas kitas įeinančias užklausas. Šiek tiek pažangesniems naudojimo atvejams galite naudoti socketserver.ThreadingMixIn
, kad sukurtumėte daugiagyslį serverį:
from socketserver import ThreadingMixIn
from http.server import HTTPServer
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
pass
# In your main block, use this instead of HTTPServer:
# webServer = ThreadingHTTPServer((hostName, serverPort), MyServer)
Nors tai padeda su lygiagretumu, jis vis dar nėra skirtas didelio našumo, didelio srauto gamybos aplinkoms. Pilnai išplėtoti žiniatinklio karkasai ir programų serveriai (tokie kaip Gunicorn arba Uvicorn) yra optimizuoti našumui, išteklių valdymui ir mastelio keitimui.
2. Saugumas
http.server
nėra sukurtas su saugumu kaip pagrindiniu tikslu. Jam trūksta integruotos apsaugos nuo įprastų žiniatinklio pažeidžiamumų, tokių kaip Cross-Site Scripting (XSS), Cross-Site Request Forgery (CSRF) arba SQL injekcijos. Gamybos lygio karkasai, tokie kaip Django, Flask ir FastAPI, suteikia šias apsaugas iškart.
3. Funkcijos ir abstrakcija
Kai jūsų programa auga, norėsite tokių funkcijų kaip duomenų bazės integravimas (ORM), šablonų varikliai, sudėtingas maršrutizavimas, vartotojų autentifikavimas ir tarpinė programinė įranga. Nors visa tai galėtumėte sukurti patys ant http.server
, iš esmės iš naujo išrastumėte žiniatinklio karkasą. Tokie karkasai kaip Flask, Django ir FastAPI pateikia šiuos komponentus gerai struktūruotu, kovos patikrintu ir prižiūrimu būdu.
Naudokite http.server
, kai:
- Mokotės ir suprantate HTTP.
- Greitas prototipų kūrimas ir koncepcijų įrodymas.
- Kuriate paprastus, tik vidinius įrankius arba informacijos suvestines.
- Kuriate fiktyvius API serverius priekinės dalies kūrimui.
- Lengvi duomenų rinkimo galiniai taškai IoT arba scenarijams.
Pereikite prie karkaso, kai:
- Viešai prieinamos žiniatinklio programos.
- Sudėtingos API su autentifikavimu ir sąveika su duomenų baze.
- Programos, kuriose saugumas, našumas ir mastelio keitimas yra kritiniai.
Išvada: Paprastumo ir valdymo galia
Python http.server
yra kalbos praktinio dizaino įrodymas. Jis suteikia paprastą, bet galingą pagrindą visiems, kuriems reikia dirbti su žiniatinklio protokolais. Išmokę tinkinti jo užklausų tvarkykles, įgyjate smulkų valdymą užklausos-atsakymo ciklui, leisdami kurti platų naudingų įrankių spektrą be viso žiniatinklio karkaso.
Kitą kartą, kai jums prireiks greitos žiniatinklio paslaugos, fiktyvaus API arba tiesiog norėsite eksperimentuoti su HTTP, prisiminkite šį universalų modulį. Tai daugiau nei tik failų serveris; tai tuščia drobė jūsų žiniatinklio pagrindu sukurtiems kūriniams, įtraukta tiesiai į Python standartinę biblioteką.