Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Efter att du byggt en FastAPI-applikation med mssql-python, konfigurera den för distribution, återanvändning av anslutningar, felhantering, autentisering och automatiserad testning.
Förutsättningar
Fullständigt Använd mssql-python med FastAPI, eller ha en motsvarande FastAPI-applikation som använder AdventureWorksLT:s exempeldatabas. Beroendet för autentisering i den här artikeln frågar efter
SalesLT.Customer.Installera produktions- och testberoenden:
pip install pydantic-settings pyjwt pytest httpx
Konfigurera inställningar för distribution
Använd Pydantic Settings för att ladda deployment-specifika värden från miljövariabler. Denna metod håller hemligheter utanför källkoden och ger varje miljö sin egen databas, pool och autentiseringskonfiguration.
Skapa 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"
)
Sätt DATABASE_SERVER, DATABASE_NAME, och JWT_SECRET i distributionsmiljön. Pydantic Settings läser automatiskt in miljövariabelnamn skrivna med versaler.
Note
ActiveDirectoryDefault försöker flera autentiseringsleverantörer i tur och ordning. I produktion, ange autentiseringsläget för den distribuerade identiteten, till exempel ActiveDirectoryMSI för managed identity, för att undvika att man går igenom credential-kedjan. För tillgängliga spellägen, se Microsoft Entra-autentisering med mssql-python.
Konfigurera anslutningspooler
MSSQL-Python möjliggör anslutningspoolning som standard. Konfigurera poolen en gång, innan applikationen skapar sin första anslutning. Storlek på poolen för applikationens förväntade samtidiga databasarbete och databasservicenivån.
Uppdatera database.py för att använda distributionsinställningarna:
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
Kontexthanteraren för anslutningen bekräftar efter att begäran har behandlats utan fel, rullar tillbaka om behandlingen av begäran utlöser ett undantag, och stänger anslutningen. Att stänga anslutningen returnerar den till poolen. För poolnycklar, dimensionering, identitetsisolering och vägledning om uttömning, se Anslutningspoolning med mssql-python.
Hantera databasfel
Registrera undantagshanterare så att databasfel returnerar konsekventa svar utan att exponera anslutningsdetaljer, frågor eller serverfeltext.
Lägg till hanterarna efter app = FastAPI(...) i 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",
},
)
Logga undantaget genom applikationens skyddade telemetripipeline innan du returnerar svaret. För undantagshierarkin och SQLSTATE-hantering, se Felhantering och SQLSTATE-koder för mssql-python.
Lägg till autentiseringsberoenden
Kedja ihop FastAPI-beroenden för att validera en JSON Web Token (JWT), läsa in motsvarande AdventureWorksLT-kund och göra kunden tillgänglig för skyddade rutter. Validera token innan du skaffar en databasanslutning så att en ogiltig token inte använder en poolad anslutning.
Skapa 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,
}
Importera beroendet och lägg till en skyddad rutt till main.py:
from auth import get_current_customer
@app.get("/me")
def get_me(current_customer: dict = Depends(get_current_customer)):
return current_customer
Använd en identitetsleverantör för att utfärda och rotera signeringsnycklar. För HS256, sätt JWT_SECRET till minst 32 slumpmässiga byte. Spara inte en produktionssigneringshemlighet i arkivet eller i en bild.
Testa programmet
FastAPI TestClient skickar förfrågningar till applikationen utan att starta en HTTP-server. Följande integrationstester använder den konfigurerade databasen.
Skapa 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"
Kör testerna från projektets rot:
pytest
Dessa tester använder den konfigurerade databasen och test_create_product infogar en rad i SalesLT.Product. Använd en dedikerad testdatabas och återställ dess data mellan testkörningarna.
Checklista för distribution
- Ange
DATABASE_SERVER,DATABASE_NAMEochJWT_SECRETvia driftsättningsplattformens hemlighetshantering och konfigurationslagring. - Använd en dedikerad Microsoft Entra-identitet med de minsta nödvändiga databasbehörigheterna.
- Ställ in poolstorleken under databasens anslutningsgräns och lämna kapacitet för administrativ åtkomst och andra arbetsbelastningar.
- Kör databasintegrationstester mot en isolerad testdatabas.
- Konfigurera skyddad telemetri för databasundantag, förfrågningslatens och poolutmattning.
- Kör Uvicorn utan
--reloadi deployerade miljöer.