Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poté, co postavíte FastAPI aplikaci pomocí mssql-python, nakonfigurujte ji pro nasazení, opětovné použití připojení, zpracování chyb, autentizaci a automatizované testování.
Předpoklady
Kompletně používejte mssql-python s FastAPI, nebo mějte ekvivalentní aplikaci FastAPI, která využívá ukázkovou databázi AdventureWorksLT. Závislost ověřování v tomto článku se dotazuje na
SalesLT.Customer.Nainstalujte produkční a testovací závislosti:
pip install pydantic-settings pyjwt pytest httpx
Konfigurace nastavení nasazení
Použijte Pydantic Settings k načtení hodnot specifických pro nasazení z proměnných prostředí. Tento přístup udržuje tajemství mimo zdrojový kód a každému prostředí poskytuje vlastní databázi, pool a konfiguraci autentizace.
Vytvořit config.py:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_server: str
database_name: str
pool_size: int = 20
pool_idle_timeout: int = 300
jwt_secret: str
settings = Settings()
def get_connection_string() -> str:
return (
f"Server={settings.database_server};"
f"Database={settings.database_name};"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes"
)
Nastavte v prostředí nasazení DATABASE_SERVER, DATABASE_NAME a JWT_SECRET. Pydantic Settings automaticky čte názvy proměnných prostředí velkými písmeny.
Note
ActiveDirectoryDefault zkouší více poskytovatelů přihlašovacích údajů za sebou. V produkci specifikujte autentizační režim pro nasazenou identitu, například ActiveDirectoryMSI pro spravovanou identitu, abyste se vyhnuli procházení řetězce přihlašovacích údajů. Pro dostupné režimy viz Ověřování Microsoft Entra pomocí mssql-python.
Konfigurace sdružování připojení
mssql-python ve výchozím nastavení umožňuje poolování spojení. Konfigurujte pool jednou, než aplikace vytvoří první připojení. Nastavte velikost fondu připojení podle očekávaného souběžného zatížení databáze aplikací a úrovně databázové služby.
Aktualizace database.py pro použití nastavení nasazení:
from collections.abc import Generator
import mssql_python
from config import get_connection_string, settings
mssql_python.pooling(
max_size=settings.pool_size,
idle_timeout=settings.pool_idle_timeout,
)
def get_db_dependency() -> Generator:
with mssql_python.connect(get_connection_string()) as conn:
with conn.cursor() as cursor:
yield cursor
Správce kontextu připojení po úspěšném zpracování požadavku potvrdí transakci, pokud zpracování požadavku vyvolá výjimku, provede rollback a uzavře spojení. Po uzavření spojení se vrací do poolu. Klíče fondu připojení, dimenzování, izolaci identit a doporučení ohledně vyčerpání najdete v tématu Connection pooling with mssql-python.
Řešení chyb v databázi
Zaregistrujte obslužné nástroje pro výjimky, aby selhání databáze vracela konzistentní odpovědi bez zveřejňování detailů spojení, dotazů nebo chybového textu serveru.
Přidejte obslužné rutiny za app = FastAPI(...) v main.py:
import mssql_python
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(mssql_python.IntegrityError)
async def integrity_exception_handler(
request: Request,
exc: mssql_python.IntegrityError,
):
return JSONResponse(
status_code=409,
content={
"detail": "The request conflicts with existing data.",
"type": "integrity_error",
},
)
@app.exception_handler(mssql_python.DatabaseError)
async def database_exception_handler(
request: Request,
exc: mssql_python.DatabaseError,
):
return JSONResponse(
status_code=500,
content={
"detail": "A database operation failed.",
"type": "database_error",
},
)
Před vrácením odpovědi zaznamenejte výjimku prostřednictvím chráněného kanálu telemetrie vaší aplikace. Pro hierarchii výjimek a zpracování SQLSTATE viz Zpracování chyb a kódy SQLSTATE pro mssql-python.
Přidat autentizační závislosti
Propojte závislosti FastAPI tak, aby ověřily token JWT (JSON Web Token), načetly odpovídajícího zákazníka AdventureWorksLT a zpřístupnily tohoto zákazníka chráněným trasám. Ověřte token před navázáním připojení k databázi, aby neplatný token nevyužil připojení z poolu.
Vytvořit auth.py:
import jwt
from fastapi import Depends, HTTPException
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from config import settings
from database import get_db_dependency
security = HTTPBearer()
def get_customer_id(
credentials: HTTPAuthorizationCredentials = Depends(security),
) -> int:
try:
payload = jwt.decode(
credentials.credentials,
settings.jwt_secret,
algorithms=["HS256"],
)
customer_id = int(payload["sub"])
except (KeyError, TypeError, ValueError):
raise HTTPException(status_code=401, detail="Invalid token subject")
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
return customer_id
def get_current_customer(
customer_id: int = Depends(get_customer_id),
cursor = Depends(get_db_dependency),
):
cursor.execute(
"""
SELECT CustomerID, FirstName, LastName
FROM SalesLT.Customer
WHERE CustomerID = %(id)s
""",
{"id": customer_id},
)
customer = cursor.fetchone()
if customer is None:
raise HTTPException(status_code=401, detail="Customer not found")
return {
"id": customer.CustomerID,
"first_name": customer.FirstName,
"last_name": customer.LastName,
}
Importujte závislost a přidejte chráněnou trasu do main.py:
from auth import get_current_customer
@app.get("/me")
def get_me(current_customer: dict = Depends(get_current_customer)):
return current_customer
Používejte poskytovatele identity k vydávání a rotaci podpisových klíčů. Pro HS256 nastavte JWT_SECRET na alespoň 32 náhodných bajtů. Neukládejte tajný klíč pro produkční podepisování do repozitáře ani do image.
Testování aplikace
TestClient FastAPI odesílá požadavky aplikaci, aniž by spouštěl HTTP server. Následující integrační testy využívají nakonfigurovanou databázi.
Vytvořit test_api.py:
import uuid
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_list_products():
response = client.get("/products")
assert response.status_code == 200
data = response.json()
assert "items" in data
assert "total" in data
def test_create_product():
suffix = uuid.uuid4().hex[:8]
response = client.post(
"/products",
json={
"name": f"Test Product {suffix}",
"product_number": f"TEST-{suffix}",
"price": 19.99,
"color": "Red",
"size": "M",
"category_id": 1,
},
)
assert response.status_code == 201
data = response.json()
assert data["product_number"] == f"TEST-{suffix}"
assert data["price"] == 19.99
def test_get_product_not_found():
response = client.get("/products/99999")
assert response.status_code == 404
def test_health_check():
response = client.get("/health")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
Spusť testy z kořene projektu:
pytest
Tyto testy využívají nakonfigurovanou databázi a test_create_product vkládají řádek do SalesLT.Product. Používejte dedikovanou testovací databázi a mezi testy resetujte její data.
Kontrolní seznam nasazení
- Nastavte
DATABASE_SERVER,DATABASE_NAMEaJWT_SECRETprostřednictvím úložišť tajných klíčů a konfigurace platformy pro nasazení. - Použijte dedikovanou identitu Microsoft Entra s minimálními požadovanými oprávněními k databázi.
- Nastavte velikost poolu pod limit připojení databáze a nechte kapacitu pro administrátorský přístup a další pracovní zátěže.
- Spusť testy integrace databáze proti izolované testovací databázi.
- Nakonfigurujte chráněnou telemetrii pro výjimky databáze, latenci požadavků a vyčerpání poolu.
- Spusťte Uvicorn bez
--reloadv nasazených prostředích.