Apgūstiet FastAPI middleware no pamatiem. Šis padziļinātais ceļvedis aptver pielāgoto middleware, autentifikāciju, žurnālu glabāšanu, kļūdu apstrādi un labāko praksi robustu API veidošanai.
Python FastAPI Middleware: Visaptverošs ceļvedis pieprasījumu un atbilžu apstrādei
Mūsdienu tīmekļa izstrādes pasaulē veiktspēja, drošība un uzturēšana ir galvenās prioritātes. Python ietvars FastAPI ir ātri ieguvis popularitāti ar savu neticamo ātrumu un izstrādātājiem draudzīgajām funkcijām. Viena no tā jaudīgākajām, taču dažkārt pārprastajām funkcijām ir middleware. Middleware darbojas kā kritisks posms pieprasījumu un atbilžu apstrādes ķēdē, ļaujot izstrādātājiem izpildīt kodu, modificēt datus un nodrošināt noteikumus pirms pieprasījums sasniedz savu mērķi vai pirms atbilde tiek nosūtīta atpakaļ klientam.
Šis visaptverošais ceļvedis ir izstrādāts globālai izstrādātāju auditorijai, sākot no tiem, kas tikko sāk ar FastAPI, līdz pieredzējušiem profesionāļiem, kuri vēlas padziļināt savu izpratni. Mēs izpētīsim middleware pamatkoncepcijas, demonstrēsim, kā veidot pielāgotus risinājumus, un iziesim cauri praktiskiem, reāliem lietošanas gadījumiem. Līdz beigām jūs būsiet aprīkoti, lai izmantotu middleware, veidojot robustākus, drošākus un efektīvākus API.
Kas ir Middleware tīmekļa ietvaru kontekstā?
Pirms iedziļināties kodā, ir svarīgi saprast koncepciju. Iedomājieties savas lietojumprogrammas pieprasījumu-atbilžu ciklu kā cauruļvadu vai montāžas līniju. Kad klients nosūta pieprasījumu uz jūsu API, tas neuzreiz sasniedz jūsu beigu punkta loģiku. Tā vietā tas iziet cauri vairākiem apstrādes posmiem. Līdzīgi, kad jūsu beigu punkts ģenerē atbildi, tā pirms sasniegšanas klienta ceļ atpakaļ cauri šiem posmiem. Middleware komponenti ir šie paši posmi cauruļvadā.
Populārs analogs ir sīpolu modelis. Sīpolu kodols ir jūsu lietojumprogrammas biznesa loģika (beigu punkts). Katrs sīpolu slānis, kas ieskauj kodolu, ir middleware daļa. Pieprasījumam ir jāizslāņojas cauri katram ārējam slānim, lai nokļūtu līdz kodolam, un atbilde ceļ atpakaļ cauri tiem pašiem slāņiem. Katrs slānis var pārbaudīt un modificēt pieprasījumu tā ienākšanas laikā un atbildi tā iziešanas laikā.
Būtībā middleware ir funkcija vai klase, kurai ir piekļuve pieprasījuma objektam, atbildes objektam un nākamajam middleware lietojumprogrammas pieprasījumu-atbilžu ciklā. Tās galvenie mērķi ietver:
- Koda izpilde: Veiciet darbības katram ienākošajam pieprasījumam, piemēram, žurnālu glabāšanu vai veiktspējas uzraudzību.
- Pieprasījuma un atbildes modificēšana: Pievienojiet galvenes, saspiest atbildes ķermeņus vai transformējiet datu formātus.
- Cikla pārtraukšana: Pārtrauciet pieprasījumu-atbilžu ciklu agrāk. Piemēram, autentifikācijas middleware var bloķēt neautentificētu pieprasījumu pirms tas sasniedz paredzēto beigu punktu.
- Globālo problēmu pārvaldība: Centralizētā vietā apstrādājiet kopīgas problēmas, piemēram, kļūdu apstrādi, CORS (Cross-Origin Resource Sharing) un sesiju pārvaldību.
FastAPI ir veidots virs Starlette rīkkopas, kas nodrošina robustu ASGI (Asynchronous Server Gateway Interface) standarta ieviešanu. Middleware ir fundamentāla koncepcija ASGI, padarot to par pirmsklases pilsoni FastAPI ekosistēmā.
Vienkāršākā forma: FastAPI Middleware ar dekoratoru
FastAPI nodrošina vienkāršu veidu, kā pievienot middleware, izmantojot dekoratoru @app.middleware("http"). Tas ir ideāli piemērots vienkāršai, pašpietiekamai loģikai, kas nepieciešama katram HTTP pieprasījumam.
Izveidosim klasisku piemēru: middleware katra pieprasījuma apstrādes laika aprēķināšanai un tā pievienošanai atbildes galvenēm. Tas ir neticami noderīgi veiktspējas uzraudzībai.
Piemērs: Process-Time Middleware
Vispirms pārliecinieties, ka jums ir instalēts FastAPI un ASGI serveris, piemēram, Uvicorn:
pip install fastapi uvicorn
Tagad uzrakstīsim kodu failā main.py:
import time
from fastapi import FastAPI, Request
app = FastAPI()
# Definēt middleware funkciju
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
# Ierakstīt sākuma laiku, kad pieprasījums ienāk
start_time = time.time()
# Turpināt uz nākamo middleware vai beigu punktu
response = await call_next(request)
# Aprēķināt apstrādes laiku
process_time = time.time() - start_time
# Pievienot pielāgoto galveni atbildei
response.headers["X-Process-Time"] = str(process_time)
return response
@app.get("/")
async def root():
# Simulēt kādu darbu
time.sleep(0.5)
return {"message": "Hello, World!"}
Lai palaistu šo lietojumprogrammu, izmantojiet komandu:
uvicorn main:app --reload
Tagad, ja nosūtīsiet pieprasījumu uz http://127.0.0.1:8000, izmantojot rīku, piemēram, cURL, vai API klientu, piemēram, Postman, atbildē redzēsiet jaunu galveni X-Process-Time ar vērtību aptuveni 0,5 sekundes.
Koda sadalījums:
@app.middleware("http"): Šis dekorators reģistrē mūsu funkciju kā HTTP middleware.async def add_process_time_header(request: Request, call_next):: Middleware funkcijai jābūt asinhronai. Tā saņem ienākošoRequestobjektu un īpašu funkcijucall_next.response = await call_next(request): Šī ir vissvarīgākā rinda.call_nextnodod pieprasījumu nākamajam posmam ciklā (cits middleware vai faktiskā ceļa operācija). Jums obligāti jāizmantoawaitšim izsaukumam. Rezultāts irResponseobjekts, ko ģenerējis beigu punkts.response.headers[...] = ...: Pēc tam, kad atbilde ir saņemta no beigu punkta, mēs to varam modificēt, šajā gadījumā pievienojot pielāgotu galveni.return response: Visbeidzot, modificētā atbilde tiek atgriezta, lai tiktu nosūtīta klientam.
Savas pielāgotās middleware izveide ar klasēm
Lai gan dekoratora pieeja ir vienkārša, tā var kļūt ierobežojoša sarežģītākām situācijām, īpaši, ja jūsu middleware nepieciešama konfigurācija vai tā nepieciešams pārvaldīt kādu iekšējo stāvokli. Šiem gadījumiem FastAPI (caur Starlette) atbalsta uz klasēm balstītu middleware, izmantojot BaseHTTPMiddleware.
Uz klasēm balstīta pieeja piedāvā labāku struktūru, ļauj injicēt atkarības tās konstruktorā un parasti ir vieglāk uzturējama sarežģītai loģikai. Galvenā loģika atrodas asinhronā dispatch metodē.
Piemērs: Uz klasēm balstīta API atslēgas autentifikācijas middleware
Izveidosim praktiskāku middleware, kas nodrošina mūsu API. Tā pārbaudīs noteiktu galveni X-API-Key, un, ja atslēga nebūs klāt vai tā būs nederīga, tā nekavējoties atgriezīs 403 Forbidden kļūdas atbildi. Tas ir piemērs pieprasījuma "pārtraukšanai cikla laikā".
main.py:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.responses import Response
# Derīgu API atslēgu saraksts. Reālā lietojumprogrammā tas nāktu no datubāzes vai drošas glabātuves.
VALID_API_KEYS = ["my-super-secret-key", "another-valid-key"]
class APIKeyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
api_key = request.headers.get("X-API-Key")
if api_key not in VALID_API_KEYS:
# Pārtraukt pieprasījumu un atgriezt kļūdas atbildi
return JSONResponse(
status_code=403,
content={"detail": "Forbidden: Invalid or missing API Key"}
)
# Ja atslēga ir derīga, turpināt ar pieprasījumu
response = await call_next(request)
return response
app = FastAPI()
# Pievienot middleware lietojumprogrammai
app.add_middleware(APIKeyMiddleware)
@app.get("/")
async def root():
return {"message": "Welcome to the secure zone!"}
Tagad, kad palaižat šo lietojumprogrammu:
- Pieprasījums bez
X-API-Keygalvenes (vai ar nepareizu vērtību) saņems 403 statusa kodu un JSON kļūdas ziņojumu. - Pieprasījums ar galveni
X-API-Key: my-super-secret-keybūs veiksmīgs un saņems 200 OK atbildi.
Šis modelis ir ārkārtīgi spēcīgs. / beigu punkta kodam nav nepieciešams zināt neko par API atslēgu validāciju; šī problēma ir pilnībā atdalīta middleware slānī.
Bieži sastopami un jaudīgi lietošanas gadījumi middleware
Middleware ir ideāls rīks kopīgu problēmu risināšanai. Apskatīsim dažus no visizplatītākajiem un ietekmīgākajiem lietošanas gadījumiem.
1. Centralizēta žurnālu glabāšana
Visaptveroša žurnālu glabāšana ir obligāta ražošanas lietojumprogrammām. Middleware ļauj jums izveidot vienu punktu, kurā jūs reģistrējat kritisko informāciju par katru pieprasījumu un tā atbildi.
Piemēra žurnālu glabāšanas middleware:
import logging
from fastapi import FastAPI, Request
import time
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
@app.middleware("http")
async def logging_middleware(request: Request, call_next):
start_time = time.time()
# Reģistrēt pieprasījuma informāciju
logger.info(f"Incoming request: {request.method} {request.url.path}")
response = await call_next(request)
process_time = time.time() - start_time
# Reģistrēt atbildes informāciju
logger.info(f"Response status: {response.status_code} | Process time: {process_time:.4f}s")
return response
Šī middleware pievienošanas brīdī reģistrē pieprasījuma metodi un ceļu, bet iziešanas brīdī atbildes statusa kodu un kopējo apstrādes laiku. Tas nodrošina nenovērtējamu ieskatu jūsu lietojumprogrammas trafīkā.
2. Globāla kļūdu apstrāde
Pēc noklusējuma neapstrādāta izņēmums jūsu kodā radīs 500 Iekšējās servera kļūdas statusu, potenciāli atklājot kļūdu stekus un implementācijas detaļas klientam. Globāla kļūdu apstrādes middleware var notvert visus izņēmumus, reģistrēt tos iekšējai pārskatīšanai un atgriezt standartizētu, lietotājam draudzīgu kļūdas atbildi.
Piemēra kļūdu apstrādes middleware:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import logging
logger = logging.getLogger(__name__)
app = FastAPI()
@app.middleware("http")
async def error_handling_middleware(request: Request, call_next):
try:
return await call_next(request)
except Exception as e:
logger.error(f"An unhandled error occurred: {e}", exc_info=True)
return JSONResponse(
status_code=500,
content={"detail": "An internal server error occurred. Please try again later."}
)
@app.get("/error")
async def cause_error():
return 1 / 0 # Tas izraisīs ZeroDivisionError
Ar šo middleware, pieprasījums uz /error vairs neizraisīs servera krahs vai neatklās steku. Tā vietā tas gracefully atgriezīs 500 statusa kodu ar tīru JSON ķermeni, kamēr pilna kļūda tiks reģistrēta servera pusē, lai izstrādātāji to varētu izmeklēt.
3. CORS (Cross-Origin Resource Sharing)
Ja jūsu priekšgala lietojumprogramma tiek apkalpota no cita domēna, protokola vai porta nekā jūsu FastAPI backend, pārlūkprogrammas bloķēs pieprasījumus vienas izcelsmes politikas dēļ. CORS ir mehānisms šīs politikas atvieglošanai. FastAPI nodrošina īpašu, ļoti konfigurējamu `CORSMiddleware` tieši šim nolūkam.
Piemēra CORS konfigurācija:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# Definēt atļauto izcelsmju sarakstu. Izmantojiet "*", ja publisks API, bet precīzi norādiet labākai drošībai.
origins = [
"http://localhost:3000",
"https://my-production-frontend.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True, # Ļaut sīkdatnes iekļaut starpizcelsmes pieprasījumos
allow_methods=["*"] # Atļaut visas standarta HTTP metodes
allow_headers=["*"] # Atļaut visas galvenes
)
Šī ir viena no pirmajām middleware daļām, ko jūs, iespējams, pievienosiet jebkuram projektam ar atdalītu priekšgalu, padarot vienkāršu starpizcelsmes politiku pārvaldīšanu no vienas, centrālās atrašanās vietas.
4. GZip saspiešana
HTTP atbilžu saspiešana var ievērojami samazināt to izmēru, nodrošinot ātrāku ielādes laiku klientiem un zemākus joslas platuma izdevumus. FastAPI ietver `GZipMiddleware` šīs automātiskai apstrādei.
Piemēra GZip middleware:
from fastapi import FastAPI
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
# Pievienot GZip middleware. Varat iestatīt minimālo izmēru saspiešanai.
app.add_middleware(GZipMiddleware, minimum_size=1000)
@app.get("/")
async def root():
# Šī atbilde ir maza un netiks saspiesta.
return {"message": "Hello World"}
@app.get("/large-data")
async def large_data():
# Šī lielā atbilde tiks automātiski saspiesta ar middleware.
return {"data": "a_very_long_string..." * 1000}
Ar šo middleware jebkura atbilde, kas lielāka par 1000 baitiem, tiks saspiesta, ja klients norādīs, ka pieņem GZip kodējumu (ko praktiski visas mūsdienu pārlūkprogrammas un klienti dara).
Papildu koncepcijas un labākā prakse
Kļūstot prasmīgākam ar middleware, ir svarīgi saprast dažus nianšu un labāko praksi, lai rakstītu tīru, efektīvu un prognozējamu kodu.
1. Middleware secībai ir nozīme!
Šis ir vissvarīgākais noteikums, kas jāatceras. Middleware tiek apstrādāta tādā secībā, kādā tā tiek pievienota lietojumprogrammai. Pirmā pievienotā middleware ir "sīpola" ārējais slānis.
Apsveriet šādu iestatījumu:
app.add_middleware(ErrorHandlingMiddleware) # Ārējais
app.add_middleware(LoggingMiddleware)
app.add_middleware(AuthenticationMiddleware) # Iekšējais
Pieprasījuma plūsma būtu šāda:
ErrorHandlingMiddlewaresaņem pieprasījumu. Tas iesaiņo savucall_nexttry...exceptblokā.- Tas izsauc
next, nododot pieprasījumuLoggingMiddleware. LoggingMiddlewaresaņem pieprasījumu, to reģistrē un izsaucnext.AuthenticationMiddlewaresaņem pieprasījumu, pārbauda akreditācijas datus un izsaucnext.- Pieprasījums beidzot sasniedz beigu punktu.
- Beigu punkts atgriež atbildi.
AuthenticationMiddlewaresaņem atbildi un nodod to tālāk.LoggingMiddlewaresaņem atbildi, to reģistrē un nodod to tālāk.ErrorHandlingMiddlewaresaņem galīgo atbildi un atgriež to klientam.
Šī secība ir loģiska: kļūdu apstrādātājs ir ārpusē, lai tas varētu notvert kļūdas no jebkura turpmākā slāņa, ieskaitot pārējo middleware. Autentifikācijas slānis ir dziļi iekšpusē, tāpēc mēs netērējam laiku, reģistrējot vai apstrādājot pieprasījumus, kas tiks noraidīti.
2. Datu nodošana, izmantojot request.state
Dažreiz middleware ir nepieciešams nodot informāciju beigu punktam. Piemēram, autentifikācijas middleware varētu dekodēt JWT un izgūt lietotāja ID. Kā tā var padarīt šo lietotāja ID pieejamu ceļa operācijas funkcijai?
Nepareizs veids ir tieši modificēt pieprasījuma objektu. Pareizais veids ir izmantot request.state objektu. Tas ir vienkāršs, tukšs objekts, kas paredzēts tieši šim nolūkam.
Piemērs: Lietotāja datu nodošana no middleware
# Jūsu autentifikācijas middleware dispatch metodē:
# ... pēc apliecības validācijas un lietotāja dekodēšanas ...
user_data = {"id": 123, "username": "global_dev"}
request.state.user = user_data
response = await call_next(request)
# Jūsu beigu punktā:
@app.get("/profile")
async def get_user_profile(request: Request):
current_user = request.state.user
return {"profile_for": current_user}
Tas saglabā loģiku tīru un izvairās no Request objekta vārdu telpas piesārņošanas.
3. Veiktspējas apsvērumi
Lai gan middleware ir jaudīgs, katrs slānis pievieno nelielu papildu slodzi. Augstas veiktspējas lietojumprogrammām paturiet prātā šos punktus:
- Saglabāt liesu: Middleware loģikai jābūt pēc iespējas ātrai un efektīvai.
- Esiet asinhroni: Ja jūsu middleware nepieciešams veikt I/O darbības (piemēram, datubāzes pārbaudi), pārliecinieties, ka tā ir pilnībā
async, lai netiktu bloķēts servera notikumu cikls. - Izmantot ar mērķi: Nepievienojiet middleware, kas jums nav nepieciešams. Katrs no tiem palielina zvanu steku dziļumu un apstrādes laiku.
4. Jūsu middleware testēšana
Middleware ir būtiska jūsu lietojumprogrammas loģikas daļa, un tā ir rūpīgi jātestē. FastAPI `TestClient` padara to vienkāršu. Varat rakstīt testus, kas nosūta pieprasījumus ar un bez nepieciešamajiem nosacījumiem (piemēram, ar un bez derīgas API atslēgas) un apgalvo, ka middleware darbojas, kā paredzēts.
Piemēra tests APIKeyMiddleware:
from fastapi.testclient import TestClient
from .main import app # Importējiet savu FastAPI lietotni
client = TestClient(app)
def test_request_without_api_key_is_forbidden():
response = client.get("/")
assert response.status_code == 403
assert response.json() == {"detail": "Forbidden: Invalid or missing API Key"}
def test_request_with_valid_api_key_is_successful():
headers = {"X-API-Key": "my-super-secret-key"}
response = client.get("/", headers=headers)
assert response.status_code == 200
assert response.json() == {"message": "Welcome to the secure zone!"}
Noslēgums
FastAPI middleware ir fundamentāls un jaudīgs rīks ikvienam izstrādātājam, kas veido modernus API. Tas nodrošina elegantu un atkārtoti lietojamu veidu, kā apstrādāt kopīgas problēmas, atdalot tās no jūsu galvenās biznesa loģikas. Pārtverot un apstrādājot katru pieprasījumu un atbildi, middleware ļauj ieviest robustu žurnālu glabāšanu, centralizētu kļūdu apstrādi, stingras drošības politikas un veiktspējas uzlabojumus, piemēram, saspiešanu.
No vienkāršā @app.middleware("http") dekoratora līdz sarežģītiem, uz klasēm balstītiem risinājumiem, jums ir elastība izvēlēties pareizo pieeju savām vajadzībām. Izprotot pamatkoncepcijas, bieži sastopamos lietošanas gadījumus un labāko praksi, piemēram, middleware secību un stāvokļa pārvaldību, jūs varat veidot tīrākas, drošākas un ļoti uzturējamas FastAPI lietojumprogrammas.
Tagad ir jūsu kārta. Sāciet integrēt pielāgotu middleware savā nākamajā FastAPI projektā un atbloķējiet jaunu kontroles un elegances līmeni savā API dizainā. Iespējas ir plašas, un šīs funkcijas apgūšana neapšaubāmi padarīs jūs par efektīvāku izstrādātāju.