Sukelduge FastAPI sõltuvuste sissepritsesse. Õppige täiustatud tehnikaid, kohandatud sõltuvusi, ulatust ja testimist, et luua robustseid API-sid.
FastAPI sõltuvussüsteem: täiustatud sõltuvuste sissepritse
FastAPI sõltuvuste sissepritse (DI) süsteem on selle disaini nurgakivi, edendades modulaarsust, testitavust ja taaskasutatavust. Kuigi põhikasutus on lihtne, avab täiustatud DI-tehnikate valdamine märkimisväärse võimsuse ja paindlikkuse. See artikkel süveneb FastAPI täiustatud sõltuvuste sissepritsesse, hõlmates kohandatud sõltuvusi, ulatust, testimisstrateegiaid ja parimaid tavasid.
Põhitõdede mõistmine
Enne täiustatud teemade juurde asumist vaatame kiirelt üle FastAPI sõltuvuste sissepritse põhitõed:
- Sõltuvused funktsioonidena: Sõltuvused deklareeritakse tavaliste Pythoni funktsioonidena.
- Automaatne sissepritse: FastAPI sisestab need sõltuvused automaatselt teekonnaoperatsioonidesse tüübihintide põhjal.
- Tüübihinded lepingutena: Tüübihinded määratlevad sõltuvuste ja teekonnaoperatsioonide funktsioonide oodatud sisendtüübid.
- Hierarhilised sõltuvused: Sõltuvused võivad sõltuda teistest sõltuvustest, luues sõltuvuspuu.
Siin on lihtne näide:
from fastapi import FastAPI, Depends
app = FastAPI()
def get_db():
db = {"items": []}
try:
yield db
finally:
# Close the connection if needed
pass
@app.get("/items/")
async def read_items(db: dict = Depends(get_db)):
return db["items"]
Selles näites on get_db sõltuvus, mis pakub andmebaasi ühendust. FastAPI kutsub automaatselt välja get_db ja sisestab tulemuse funktsiooni read_items.
Täiustatud sõltuvustehnikad
1. Klasside kasutamine sõltuvustena
Kuigi funktsioone kasutatakse tavaliselt, võivad klassid samuti toimida sõltuvustena, võimaldades keerukamat olekuhaldust ja meetodeid. See on eriti kasulik andmebaasiühenduste, autentimisteenuste või muude ressursside puhul, mis vajavad initsialiseerimist ja puhastamist.
from fastapi import FastAPI, Depends
app = FastAPI()
class Database:
def __init__(self):
self.connection = self.create_connection()
def create_connection(self):
# Simulate a database connection
print("Creating database connection...")
return {"items": []}
def close(self):
# Simulate closing a database connection
print("Closing database connection...")
def get_db():
db = Database()
try:
yield db.connection
finally:
db.close()
@app.get("/items/")
async def read_items(db: dict = Depends(get_db)):
return db["items"]
Selles näites kapseldab klass Database andmebaasi ühendusloogika. Sõltuvus get_db loob klassi Database isendi ja tagastab ühenduse. Plokk finally tagab, et ühendus suletakse korralikult pärast päringu töötlemist.
2. Sõltuvuste tühistamine
FastAPI võimaldab sõltuvusi tühistada, mis on testimise ja arenduse jaoks ülioluline. Saate asendada tõelise sõltuvuse moki või stubiga, et isoleerida oma koodi ja tagada järjepidevad tulemused.
from fastapi import FastAPI, Depends
app = FastAPI()
# Original dependency
def get_settings():
# Simulate loading settings from a file or environment
return {"api_key": "real_api_key"}
@app.get("/items/")
async def read_items(settings: dict = Depends(get_settings)):
return {"api_key": settings["api_key"]}
# Override for testing
def get_settings_override():
return {"api_key": "test_api_key"}
app.dependency_overrides[get_settings] = get_settings_override
# To revert back to the original:
# del app.dependency_overrides[get_settings]
Selles näites tühistatakse sõltuvus get_settings funktsiooniga get_settings_override. See võimaldab teil testimise eesmärgil kasutada erinevat API-võtit.
3. `contextvars` kasutamine päringupõhiste andmete jaoks
contextvars on Pythoni moodul, mis pakub kontekstipõhiseid muutujaid. See on kasulik päringuspetsiifiliste andmete, nagu kasutaja autentimisteabe, päringu ID-de või jälgimisandmete salvestamiseks. contextvars kasutamine koos FastAPI sõltuvuste sissepritsega võimaldab teil nendele andmetele kogu oma rakenduses juurde pääseda.
import contextvars
from fastapi import FastAPI, Depends, Request
app = FastAPI()
# Create a context variable for the request ID
request_id_var = contextvars.ContextVar("request_id")
# Middleware to set the request ID
@app.middleware("http")
async def add_request_id(request: Request, call_next):
request_id = str(uuid.uuid4())
request_id_var.set(request_id)
response = await call_next(request)
response.headers["X-Request-ID"] = request_id
return response
# Dependency to access the request ID
def get_request_id():
return request_id_var.get()
@app.get("/items/")
async def read_items(request_id: str = Depends(get_request_id)):
return {"request_id": request_id}
Selles näites määrab vahevara igale sissetulevale päringule unikaalse päringu ID. Sõltuvus get_request_id hangib päringu ID contextvars kontekstist. See võimaldab teil jälgida päringuid kogu oma rakenduses.
4. Asünkroonsed sõltuvused
FastAPI toetab sujuvalt asünkroonseid sõltuvusi. See on oluline mittestandardsete I/O-operatsioonide puhul, nagu andmebaasipäringud või välised API-kutsed. Lihtsalt defineerige oma sõltuvusfunktsioon kui async def funktsioon.
from fastapi import FastAPI, Depends
import asyncio
app = FastAPI()
async def get_data():
# Simulate an asynchronous operation
await asyncio.sleep(1)
return {"message": "Hello from async dependency!"}
@app.get("/items/")
async def read_items(data: dict = Depends(get_data)):
return data
Selles näites on get_data sõltuvus asünkroonne funktsioon, mis simuleerib viivitust. FastAPI ootab automaatselt asünkroonse sõltuvuse tulemust, enne kui see sisestatakse funktsiooni read_items.
5. Generaatorite kasutamine ressursside haldamiseks (andmebaasiühendused, failikäepidemed)
Generaatorite (koos yield) kasutamine pakub automaatset ressursside haldamist, tagades, et ressursid suletakse/vabastatakse korralikult finally ploki kaudu isegi vigade ilmnemisel.
from fastapi import FastAPI, Depends
app = FastAPI()
def get_file_handle():
try:
file_handle = open("my_file.txt", "r")
yield file_handle
finally:
file_handle.close()
@app.get("/file_content/")
async def read_file_content(file_handle = Depends(get_file_handle)):
content = file_handle.read()
return {"content": content}
Sõltuvuse ulatused ja elutsüklid
Sõltuvuse ulatuste mõistmine on ülioluline sõltuvuste elutsükli haldamiseks ja ressursside õige jaotamise ja vabastamise tagamiseks. FastAPI ei paku otse selgeid ulatuse annotatsioone nagu mõned teised DI raamistikud (nt Springi @RequestScope, @ApplicationScope), kuid sõltuvuste määratlemise ja oleku haldamise kombinatsioon annab sarnaseid tulemusi.
Päringu ulatus
See on kõige levinum ulatus. Iga päring saab sõltuvuse uue isendi. See saavutatakse tavaliselt uue objekti loomisega sõltuvusfunktsiooni sees ja selle tagastamisega, nagu näidati andmebaasi näites. contextvars kasutamine aitab samuti saavutada päringu ulatust.
Rakenduse ulatus (Singleton)
Sõltuvuse üks isend luuakse ja jagatakse kõikide päringute vahel kogu rakenduse elutsükli jooksul. Seda tehakse sageli globaalsete muutujate või klassitaseme atribuutide abil.
from fastapi import FastAPI, Depends
app = FastAPI()
# Singleton instance
GLOBAL_SETTING = {"api_key": "global_api_key"}
def get_global_setting():
return GLOBAL_SETTING
@app.get("/items/")
async def read_items(setting: dict = Depends(get_global_setting)):
return setting
Olge ettevaatlik rakendusepõhiste sõltuvuste kasutamisel muudetava olekuga, kuna ühe päringu tehtud muudatused võivad mõjutada teisi päringuid. Sünkroniseerimismehhanisme (lukud jne) võib vaja minna, kui teie rakendusel on samaaegseid päringuid.
Sessiooni ulatus (kasutajaspetsiifilised andmed)
Seostage sõltuvused kasutajasessioonidega. See nõuab sessioonihaldusmehhanismi (nt küpsiste või JWT-de kasutamine) ja hõlmab tavaliselt sõltuvuste salvestamist sessiooni andmetesse.
from fastapi import FastAPI, Depends, Cookie
from typing import Optional
import uuid
app = FastAPI()
# In a real app, store sessions in a database or cache
sessions = {}
async def get_user_id(session_id: Optional[str] = Cookie(None)) -> str:
if session_id is None or session_id not in sessions:
session_id = str(uuid.uuid4())
sessions[session_id] = {"user_id": str(uuid.uuid4())} # Assign a random user ID
return sessions[session_id]["user_id"]
@app.get("/profile/")
async def read_profile(user_id: str = Depends(get_user_id)):
return {"user_id": user_id}
Sõltuvuste testimine
Üks sõltuvuste sissepritse peamisi eeliseid on parem testitavus. Komponentide lahtiühendamisega saate testimise ajal hõlpsasti asendada sõltuvused mokkide või stubidega.
1. Sõltuvuste tühistamine testides
Nagu varem demonstreeritud, on FastAPI dependency_overrides mehhanism testimiseks ideaalne. Looge mock-sõltuvused, mis tagastavad ennustatavaid tulemusi, ja kasutage neid oma testimise all oleva koodi isoleerimiseks.
from fastapi.testclient import TestClient
from fastapi import FastAPI, Depends
app = FastAPI()
# Original dependency
def get_external_data():
# Simulate fetching data from an external API
return {"data": "Real external data"}
@app.get("/data/")
async def read_data(data: dict = Depends(get_external_data)):
return data
# Test
from unittest.mock import MagicMock
def get_external_data_mock():
return {"data": "Mocked external data"}
def test_read_data():
app.dependency_overrides[get_external_data] = get_external_data_mock
client = TestClient(app)
response = client.get("/data/")
assert response.status_code == 200
assert response.json() == {"data": "Mocked external data"}
# Clean up overrides
app.dependency_overrides.clear()
2. Mokkimisraamatukogude kasutamine
Raamatukogud nagu unittest.mock pakuvad võimsaid tööriistu mock-objektide loomiseks ja nende käitumise kontrollimiseks. Saate kasutada mocke keerukate sõltuvuste simuleerimiseks ja veendumiseks, et teie kood nendega õigesti suhtleb.
import unittest
from unittest.mock import MagicMock
# (Define the FastAPI app and get_external_data as above)
class TestReadData(unittest.TestCase):
def test_read_data_with_mock(self):
# Create a mock for the get_external_data dependency
mock_get_external_data = MagicMock(return_value={"data": "Mocked data from unittest"})
# Override the dependency with the mock
app.dependency_overrides[get_external_data] = mock_get_external_data
client = TestClient(app)
response = client.get("/data/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), {"data": "Mocked data from unittest"})
# Assert that the mock was called
mock_get_external_data.assert_called_once()
# Clean up overrides
app.dependency_overrides.clear()
3. Sõltuvuste sissepritse ühiktestimiseks (väljaspool FastAPI konteksti)
Isegi ühiktestimisel funktsioone *väljaspool* API lõpp-punkti käitlejaid kehtivad sõltuvuste sissepritse põhimõtted. Selle asemel, et tugineda FastAPI Depends-ile, sisestage sõltuvused käsitsi testitavasse funktsiooni.
# Example function to test
def process_data(data_source):
data = data_source.fetch_data()
# ... process the data ...
return processed_data
class MockDataSource:
def fetch_data(self):
return {"example": "data"}
# Unit test
def test_process_data():
mock_data_source = MockDataSource()
result = process_data(mock_data_source)
# Assertions on the result
Turvalisuskaalutlused sõltuvuste sissepritsega
Sõltuvuste sissepritse, kuigi kasulik, toob endaga kaasa potentsiaalseid turvalisusprobleeme, kui seda hoolikalt ei rakendata.
1. Sõltuvuste segadus
Veenduge, et tõmbate sõltuvusi usaldusväärsetest allikatest. Kontrollige paketi terviklikkust ja kasutage pakettihaldureid koos haavatavuste skaneerimise võimalustega. See on üldine tarkvara tarneahela turvalisuse põhimõte, kuid DI süvendab seda, kuna võite süstida komponente erinevatest allikatest.
2. Pahatahtlike sõltuvuste sissepritse
Olge teadlik sõltuvustest, mis aktsepteerivad välist sisendit ilma korraliku valideerimiseta. Ründaja võib potentsiaalselt süstida pahatahtlikku koodi või andmeid kompromiteeritud sõltuvuse kaudu. Puhastage kõik kasutaja sisendid ja rakendage robustseid valideerimismehhanisme.
3. Info leke sõltuvuste kaudu
Veenduge, et sõltuvused ei avaldaks tahtmatult tundlikku teavet. Vaadake üle oma sõltuvuste kood ja konfiguratsioon, et tuvastada potentsiaalsed infolekke haavatavused.
4. Koodi sisestatud saladused
Vältige saladuste (API võtmed, andmebaasi paroolid jne) otse oma sõltuvuskoodi sisestamist. Kasutage keskkonnamuutujaid või turvalisi konfiguratsioonihaldustööriistu saladuste salvestamiseks ja haldamiseks.
import os
from fastapi import FastAPI, Depends
app = FastAPI()
def get_api_key():
api_key = os.environ.get("API_KEY")
if not api_key:
raise ValueError("API_KEY environment variable not set.")
return api_key
@app.get("/secure_endpoint/")
async def secure_endpoint(api_key: str = Depends(get_api_key)):
# Use api_key for authentication/authorization
return {"message": "Access granted"}
Jõudluse optimeerimine sõltuvuste sissepritsega
Sõltuvuste sissepritse võib mõjutada jõudlust, kui seda hoolikalt ei kasutata. Siin on mõned optimeerimisstrateegiad:
1. Minimeerige sõltuvuse loomise kulu
Vältige võimalusel iga päringu puhul kallite sõltuvuste loomist. Kui sõltuvus on olekuta või seda saab jagada päringute vahel, kaaluge singleton-ulatuse kasutamist või sõltuvuse isendi vahemällu salvestamist.
2. Viitkäivitamine (Lazy Initialization)
Initsialiseerige sõltuvused ainult siis, kui neid on vaja. See võib vähendada käivitusaega ja mälutarbimist, eriti paljude sõltuvustega rakenduste puhul.
3. Sõltuvustulemuste vahemällu salvestamine
Vahemällu salvestage kallite sõltuvuste arvutuste tulemused, kui tulemusi tõenäoliselt uuesti kasutatakse. Kasutage vahemällu salvestamise mehhanisme (nt Redis, Memcached) sõltuvustulemuste salvestamiseks ja hankimiseks.
4. Optimeerige sõltuvuste graafikut
Analüüsige oma sõltuvuste graafikut, et tuvastada potentsiaalseid kitsaskohti. Lihtsustage sõltuvuste struktuuri ja vähendage sõltuvuste arvu, kui võimalik.
5. Asünkroonsed sõltuvused I/O-piiratud operatsioonide jaoks
Kasutage asünkroonseid sõltuvusi blokeerivate I/O-operatsioonide, nagu andmebaasipäringud või välised API-kutsed, teostamisel. See hoiab ära peamise lõime blokeerimise ja parandab rakenduse üldist reageerimisvõimet.
FastAPI sõltuvuste sissepritse parimad tavad
- Hoidke sõltuvused lihtsana: Püüdke luua väikeseid, fokusseeritud sõltuvusi, mis täidavad ühte ülesannet. See parandab loetavust, testitavust ja hooldatavust.
- Kasutage tüübihinte: Kasutage tüübihinte sõltuvuste oodatavate sisend- ja väljundtüüpide selgeks määratlemiseks. See parandab koodi selgust ja võimaldab FastAPI-l teha staatilist tüübikontrolli.
- Dokumenteerige sõltuvused: Dokumenteerige iga sõltuvuse eesmärk ja kasutus. See aitab teistel arendajatel mõista, kuidas teie koodi kasutada ja hooldada.
- Testige sõltuvusi põhjalikult: Kirjutage oma sõltuvustele ühiktestid, et veenduda nende ootuspärases käitumises. See aitab vältida vigu ja parandada teie rakenduse üldist töökindlust.
- Kasutage järjepidevaid nimetamiskonventsioone: Kasutage oma sõltuvuste jaoks järjepidevaid nimetamiskonventsioone, et parandada koodi loetavust.
- Vältige ringikujulisi sõltuvusi: Ringikujulised sõltuvused võivad viia keerulise ja raskesti silutava koodini. Refaktoreerige oma koodi, et kõrvaldada ringikujulised sõltuvused.
- Kaaluge sõltuvuste sissepritse konteinereid (valikuline): Kuigi FastAPI sisseehitatud sõltuvuste sissepritse on enamikul juhtudel piisav, kaaluge keerukamate rakenduste puhul spetsiaalsete sõltuvuste sissepritse konteinerite (nt
inject,autowire) kasutamist.
Järeldus
FastAPI sõltuvuste sissepritse süsteem on võimas tööriist, mis edendab modulaarsust, testitavust ja taaskasutatavust. Valdades täiustatud tehnikaid, nagu klasside kasutamine sõltuvustena, sõltuvuste tühistamine ja contextvars kasutamine, saate luua robustseid ja skaleeritavaid API-sid. Sõltuvuste ulatuste ja elutsüklite mõistmine on ressursside tõhusaks haldamiseks ülioluline. Seadke alati esikohale sõltuvuste põhjalik testimine, et tagada oma rakenduste töökindlus ja turvalisus. Järgides parimaid tavasid ning arvestades potentsiaalsete turvalisuse ja jõudluse tagajärgedega, saate FastAPI sõltuvuste sissepritse süsteemi täieliku potentsiaali ära kasutada.