Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Après avoir construit une application FastAPI avec mssql-python, configurez-la pour le déploiement, la réutilisation de connexions, la gestion des erreurs, l’authentification et les tests automatisés.
Prerequisites
Utilisez MSSQL-python avec FastAPI, ou ayez une application FastAPI équivalente qui utilise la base de données d’exemple AdventureWorksLT. La dépendance d’authentification dans cet article interroge
SalesLT.Customer.Installez les dépendances de production et de test :
pip install pydantic-settings pyjwt pytest httpx
Configurer les paramètres de déploiement
Utilisez Paramètres Pydantic pour charger des valeurs spécifiques au déploiement à partir des variables d’environnement. Cette approche maintient les secrets hors du code source et offre à chaque environnement sa propre base de données, son pool et sa configuration d’authentification.
Créez 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"
)
Définissez DATABASE_SERVER, DATABASE_NAME, et JWT_SECRET dans l’environnement de déploiement. Pydantic Settings lit automatiquement les noms de variables d’environnement en majuscules.
Note
ActiveDirectoryDefault essaie successivement plusieurs fournisseurs d’informations d’identification. En production, spécifiez le mode d’authentification de l’identité déployée, par exemple ActiveDirectoryMSI pour l’identité gérée, afin d’éviter de parcourir la chaîne des identifiants. Pour les modes disponibles, voir authentification Microsoft Entra avec mssql-python.
Configurer le regroupement de connexions
MSSQL-Python permet par défaut le pooling de connexions. Configurez le pool une fois, avant que l’application ne crée sa première connexion. Dimensionnez le pool pour le travail concurrent attendu de l’application dans la base de données et le niveau de service de la base de données.
Mise à jour database.py pour utiliser les paramètres de déploiement :
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
Le gestionnaire de contexte de connexion fait un commit après un traitement réussi des requêtes, revient en arrière lorsque le traitement des requêtes ouvre une exception, puis ferme la connexion. Fermer la connexion le ramène dans le pool. Pour les clés du pool, le dimensionnement, l’isolation des identités et les recommandations concernant l’épuisement, voir Pool de connexions avec mssql-python.
Gérer les erreurs de base de données
Enregistrez des gestionnaires d’exception afin que les défaillances de la base de données renvoient des réponses cohérentes sans exposer les détails de connexion, les requêtes ou le texte d’erreur du serveur.
Ajouter les manipulateurs après app = FastAPI(...) dans 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",
},
)
Enregistrez l’exception via le pipeline de télémétrie protégé de votre application avant de retourner la réponse. Pour la hiérarchie des exceptions et la gestion SQLSTATE, voir Gestion des erreurs et codes SQLSTATE pour mssql-python.
Ajouter des dépendances d’authentification
Enchaînez les dépendances FastAPI pour valider un JSON Web Token (JWT), charger le client AdventureWorksLT correspondant et mettre ce client à la disposition des routes protégées. Validez le jeton avant d’acquérir une connexion de base de données afin qu’un jeton invalide n’utilise pas une connexion poolée.
Créez 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,
}
Importez la dépendance et ajoutez une route protégée vers main.py:
from auth import get_current_customer
@app.get("/me")
def get_me(current_customer: dict = Depends(get_current_customer)):
return current_customer
Utilisez un fournisseur d’identité pour émettre et faire tourner les clés de signature. Pour HS256, réglez JWT_SECRET à au moins 32 octets aléatoires. Ne stockez pas un secret de signature de production dans le dépôt ou dans une image.
Tester l’application
FastAPI TestClient envoie des requêtes à l’application sans démarrer de serveur HTTP. Les tests d’intégration suivants utilisent la base de données configurée.
Créez 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"
Exécutez les tests à partir de la racine du projet :
pytest
Ces tests utilisent la base de données configurée, et test_create_product insèrent une ligne dans SalesLT.Product. Utilisez une base de données dédiée aux tests et réinitialisez ses données entre les essais.
Liste de vérification du déploiement
- Définir
DATABASE_SERVER,DATABASE_NAME, etJWT_SECRETvia les archives secrètes et de configuration de la plateforme de déploiement. - Utilisez une identité Microsoft Entra dédiée avec les autorisations minimales requises pour la base de données.
- Réglez la taille du pool en dessous de la limite de connexion de la base de données et laissez une capacité pour l’accès administratif et d’autres charges de travail.
- Effectuez des tests d’intégration de base de données sur une base de données isolée.
- Configurez la télémétrie protégée pour les exceptions de base de données, la latence des requêtes et l’épuisement du pool.
- Utilisez Uvicorn sans
--reloaden environnement déployé.