Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Después de construir una aplicación FastAPI con mssql-python, configúrala para despliegue, reutilización de conexiones, manejo de errores, autenticación y pruebas automatizadas.
Prerequisites
Completa Usar mssql-python con FastAPI, o dispón de una aplicación FastAPI equivalente que utilice la base de datos de ejemplo AdventureWorksLT. La dependencia de autenticación en este artículo consulta
SalesLT.Customer.Instala las dependencias de producción y prueba:
pip install pydantic-settings pyjwt pytest httpx
Configurar los ajustes de implementación
Usa Pydantic Settings para cargar valores específicos del despliegue a partir de variables de entorno. Este enfoque mantiene los secretos fuera del código fuente y proporciona a cada entorno su propia base de datos, pool y configuración de autenticación.
Creación de 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"
)
Establece DATABASE_SERVER, DATABASE_NAME, y JWT_SECRET en el entorno de despliegue. Pydantic Settings lee automáticamente los nombres de las variables de entorno en mayúsculas.
Nota:
ActiveDirectoryDefault prueba varios proveedores de credenciales de forma secuencial. En producción, especifica el modo de autenticación para la identidad desplegada, como ActiveDirectoryMSI para la identidad gestionada, para evitar recorrer la cadena de credenciales. Para conocer los modos disponibles, consulta Autenticación de Microsoft Entra con mssql-python.
Configuración de la agrupación de conexiones
MSSQL-Python permite la agrupación de conexiones por defecto. Configura el pool una vez, antes de que la aplicación cree su primera conexión. Dimensiona el pool para el trabajo concurrente esperado de la aplicación en bases de datos y el nivel de servicio de base de datos.
Actualiza database.py para usar la configuración de implementación:
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
El gestor de contexto de la conexión confirma después de procesar correctamente la solicitud, revierte los cambios cuando el procesamiento de la solicitud genera una excepción y cierra la conexión. Cerrar la conexión lo devuelve a la piscina. Para obtener información sobre claves de agrupación, dimensionamiento, aislamiento de identidad y recomendaciones sobre agotamiento, consulte Agrupación de conexiones en mssql-python.
Gestionar errores de base de datos
Registra controladores de excepciones para que los fallos de la base de datos devuelvan respuestas consistentes sin exponer detalles de conexión, consultas ni mensajes de error del servidor.
Añadir los manejadores después de app = FastAPI(...) en 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",
},
)
Registra la excepción a través de la tubería de telemetría protegida de tu aplicación antes de devolver la respuesta. Para la jerarquía de excepciones y el manejo de SQLSTATE, véase Gestión de errores y códigos SQLSTATE para mssql-python.
Añadir dependencias de autenticación
Encadena las dependencias de FastAPI para validar un JSON Web Token (JWT), cargar el cliente de AdventureWorksLT correspondiente y poner ese cliente a disposición de las rutas protegidas. Valida el token antes de adquirir una conexión a la base de datos para que un token inválido no use una conexión agrupada.
Creación de 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,
}
Importa la dependencia y añade una ruta protegida a main.py:
from auth import get_current_customer
@app.get("/me")
def get_me(current_customer: dict = Depends(get_current_customer)):
return current_customer
Utiliza un proveedor de identidad para emitir y rotar las claves de firma. Para HS256, configúralo JWT_SECRET en al menos 32 bytes aleatorios. No almacenes un secreto de firma de producción en el repositorio ni en una imagen.
Prueba de la aplicación
FastAPI TestClient envía solicitudes a la aplicación sin iniciar un servidor HTTP. Las siguientes pruebas de integración utilizan la base de datos configurada.
Creación de 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"
Ejecuta las pruebas desde la raíz del proyecto:
pytest
Estas pruebas utilizan la base de datos configurada e test_create_product insertan una fila en SalesLT.Product. Utiliza una base de datos específica para pruebas y restablece sus datos entre ejecuciones de prueba.
Lista de comprobación de la implementación
- Establezca
DATABASE_SERVER,DATABASE_NAME, yJWT_SECRETa través de los almacenes secretos y de configuración de la plataforma de despliegue. - Utiliza una identidad Microsoft Entra dedicada con los permisos mínimos requeridos para la base de datos.
- Establece el tamaño del pool por debajo del límite de conexión de la base de datos y deja capacidad para acceso administrativo y otras cargas de trabajo.
- Realiza pruebas de integración de bases de datos contra una base de datos aislada.
- Configura la telemetría protegida para excepciones en la base de datos, latencia de solicitudes y agotamiento de pool.
- Usa Uvicorn sin
--reloaden entornos desplegados.