Python后端API開發(fā)的完全指南
概述
在現(xiàn)代Web開發(fā)中,API(Application Programming Interface)已成為不同應(yīng)用程序之間通信的核心方式。Python憑借其簡潔的語法、豐富的生態(tài)系統(tǒng)和強大的框架,成為了后端API開發(fā)的首選語言之一。本文將全面介紹使用Python進行后端API開發(fā)的全過程,涵蓋框架選擇、路由設(shè)計、數(shù)據(jù)庫集成、認證授權(quán)、性能優(yōu)化到部署上線的各個方面。
本文將使用FastAPI框架作為示例,因為它具有高性能、易用性和自動生成API文檔等突出優(yōu)點。同時我們也會介紹Flask和Django REST framework的對比,幫助讀者根據(jù)項目需求做出合適的技術(shù)選型。
1. 后端API開發(fā)基礎(chǔ)
1.1 什么是RESTful API
REST(Representational State Transfer)是一種軟件架構(gòu)風(fēng)格,RESTful API則是遵循REST原則設(shè)計的API。它的核心特征包括:
- 無狀態(tài)性:每個請求包含處理所需的所有信息
- 統(tǒng)一接口:資源標(biāo)識、資源操作、自描述消息和超媒體作為應(yīng)用狀態(tài)引擎
- 資源導(dǎo)向:使用URI標(biāo)識資源
- 表述性:資源與它們的表述分離
一個典型的RESTful API使用HTTP方法對應(yīng)CRUD操作:
| HTTP方法 | CRUD操作 | 描述 |
|---|---|---|
| GET | Read | 獲取資源 |
| POST | Create | 創(chuàng)建資源 |
| PUT | Update | 更新資源 |
| DELETE | Delete | 刪除資源 |
1.2 Python API框架對比
Python生態(tài)系統(tǒng)中有多個優(yōu)秀的API開發(fā)框架:
1.2.1 Flask
輕量級微框架,靈活性強,擴展通過插件實現(xiàn)。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return {'message': 'Hello World!'}
1.2.2 Django REST framework
功能強大的框架,內(nèi)置許多開箱即用的功能,適合復(fù)雜項目。
from rest_framework import serializers, viewsets
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username']
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
1.2.3 FastAPI
現(xiàn)代高性能框架,支持異步,自動生成API文檔。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.post("/items/")
async def create_item(item: Item):
return item
框架選擇建議:
- 小型項目或微服務(wù):FastAPI或Flask
- 大型企業(yè)級應(yīng)用:Django REST framework
- 需要高性能和異步支持:FastAPI
2. 環(huán)境設(shè)置與項目結(jié)構(gòu)
2.1 創(chuàng)建虛擬環(huán)境
使用虛擬環(huán)境是Python開發(fā)的最佳實踐,可以隔離項目依賴。
# 創(chuàng)建虛擬環(huán)境 python -m venv myapi-env # 激活虛擬環(huán)境 (Linux/macOS) source myapi-env/bin/activate # 激活虛擬環(huán)境 (Windows) myapi-env\Scripts\activate # 安裝核心依賴 pip install fastapi uvicorn sqlalchemy psycopg2-binary python-jose[cryptography] passlib[bcrypt]
2.2 項目結(jié)構(gòu)規(guī)劃
良好的項目結(jié)構(gòu)有助于代碼維護和團隊協(xié)作。
myapi-project/ ├── app/ │ ├── __init__.py │ ├── main.py # 應(yīng)用入口點 │ ├── core/ # 核心配置 │ │ ├── __init__.py │ │ ├── config.py # 配置文件 │ │ └── security.py # 安全相關(guān) │ ├── models/ # 數(shù)據(jù)模型 │ │ ├── __init__.py │ │ └── user.py # 用戶模型 │ ├── schemas/ # Pydantic模型 │ │ ├── __init__.py │ │ └── user.py # 用戶Schema │ ├── api/ # 路由端點 │ │ ├── __init__.py │ │ ├── endpoints/ # 各個端點 │ │ │ ├── __init__.py │ │ │ ├── auth.py # 認證端點 │ │ │ └── users.py # 用戶端點 │ │ └── deps.py # 依賴項 │ ├── crud/ # 數(shù)據(jù)庫操作 │ │ ├── __init__.py │ │ └── user.py # 用戶CRUD操作 │ ├── db/ # 數(shù)據(jù)庫相關(guān) │ │ ├── __init__.py │ │ └── session.py # 數(shù)據(jù)庫會話 │ └── utils/ # 工具函數(shù) │ ├── __init__.py │ └── send_email.py # 郵件發(fā)送工具 ├── tests/ # 測試代碼 │ ├── __init__.py │ ├── conftest.py │ └── test_users.py ├── alembic/ # 數(shù)據(jù)庫遷移 │ ├── versions/ │ ├── env.py │ └── script.py.mako ├── requirements.txt # 項目依賴 └── Dockerfile # Docker配置
3. FastAPI核心概念與實現(xiàn)
3.1 創(chuàng)建FastAPI應(yīng)用
首先創(chuàng)建主應(yīng)用文件,配置基本設(shè)置。
# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.core.config import settings
from app.api import api_router
# 創(chuàng)建FastAPI應(yīng)用實例
app = FastAPI(
title=settings.PROJECT_NAME,
version=settings.VERSION,
description=settings.DESCRIPTION,
openapi_url=f"{settings.API_V1_STR}/openapi.json"
)
# 設(shè)置CORS中間件
app.add_middleware(
CORSMiddleware,
allow_origins=settings.BACKEND_CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 包含API路由
app.include_router(api_router, prefix=settings.API_V1_STR)
@app.get("/")
async def root():
return {"message": "Welcome to MyAPI"}
3.2 配置管理
使用Pydantic的BaseSettings管理配置,支持環(huán)境變量。
# app/core/config.py
from pydantic import BaseSettings
from typing import List, Optional
import secrets
class Settings(BaseSettings):
# 項目配置
PROJECT_NAME: str = "MyAPI"
VERSION: str = "1.0.0"
DESCRIPTION: str = "A powerful API built with FastAPI"
API_V1_STR: str = "/api/v1"
# 安全配置
SECRET_KEY: str = secrets.token_urlsafe(32)
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 # 60 minutes * 24 hours * 8 days = 8 days
# 數(shù)據(jù)庫配置
DATABASE_URL: str = "sqlite:///./test.db"
# CORS配置
BACKEND_CORS_ORIGINS: List[str] = ["http://localhost:3000"] # 前端地址
class Config:
case_sensitive = True
env_file = ".env"
settings = Settings()
3.3 數(shù)據(jù)庫模型與遷移
使用SQLAlchemy定義數(shù)據(jù)模型,Alembic處理數(shù)據(jù)庫遷移。
# app/models/user.py
from sqlalchemy import Boolean, Column, Integer, String, DateTime
from sqlalchemy.sql import func
from app.db.session import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
full_name = Column(String, index=True)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
def __repr__(self):
return f"<User {self.email}>"
3.4 Pydantic模式(Schemas)
使用Pydantic模型進行數(shù)據(jù)驗證和序列化。
# app/schemas/user.py
from pydantic import BaseModel, EmailStr
from datetime import datetime
from typing import Optional
# 用戶基礎(chǔ)屬性
class UserBase(BaseModel):
email: Optional[EmailStr] = None
full_name: Optional[str] = None
is_active: Optional[bool] = True
is_superuser: Optional[bool] = False
# 創(chuàng)建用戶時的屬性
class UserCreate(UserBase):
email: EmailStr
password: str
# 更新用戶時的屬性
class UserUpdate(UserBase):
password: Optional[str] = None
# 數(shù)據(jù)庫中的用戶模型
class UserInDBBase(UserBase):
id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
orm_mode = True
# 返回給客戶端的用戶信息
class User(UserInDBBase):
pass
# 存儲在數(shù)據(jù)庫中的用戶信息
class UserInDB(UserInDBBase):
hashed_password: str
3.5 CRUD操作
實現(xiàn)與數(shù)據(jù)庫交互的CRUD操作。
# app/crud/user.py
from sqlalchemy.orm import Session
from app.models.user import User
from app.schemas.user import UserCreate, UserUpdate
from app.core.security import get_password_hash
def get_user(db: Session, user_id: int):
return db.query(User).filter(User.id == user_id).first()
def get_user_by_email(db: Session, email: str):
return db.query(User).filter(User.email == email).first()
def get_users(db: Session, skip: int = 0, limit: int = 100):
return db.query(User).offset(skip).limit(limit).all()
def create_user(db: Session, user: UserCreate):
hashed_password = get_password_hash(user.password)
db_user = User(
email=user.email,
hashed_password=hashed_password,
full_name=user.full_name,
is_active=user.is_active,
is_superuser=user.is_superuser
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def update_user(db: Session, user_id: int, user: UserUpdate):
db_user = db.query(User).filter(User.id == user_id).first()
if not db_user:
return None
update_data = user.dict(exclude_unset=True)
if "password" in update_data:
hashed_password = get_password_hash(update_data["password"])
del update_data["password"]
update_data["hashed_password"] = hashed_password
for field in update_data:
setattr(db_user, field, update_data[field])
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def delete_user(db: Session, user_id: int):
db_user = db.query(User).filter(User.id == user_id).first()
if not db_user:
return None
db.delete(db_user)
db.commit()
return db_user
3.6 安全與認證
實現(xiàn)JWT認證和密碼哈希。
# app/core/security.py
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from app.core.config import settings
# 密碼哈希上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# JWT令牌創(chuàng)建函數(shù)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
# 密碼驗證函數(shù)
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# 密碼哈希函數(shù)
def get_password_hash(password):
return pwd_context.hash(password)
# 從JWT令牌獲取用戶信息
def decode_access_token(token: str):
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
return payload
except JWTError:
return None
4. API端點實現(xiàn)
4.1 用戶認證端點
實現(xiàn)用戶注冊、登錄和令牌刷新功能。
# app/api/endpoints/auth.py
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from datetime import timedelta
from app.db.session import get_db
from app.core.security import create_access_token, verify_password, decode_access_token
from app.core.config import settings
from app.crud.user import get_user_by_email
from app.schemas.token import Token, TokenPayload
from app.schemas.user import User, UserCreate
from app.api.deps import get_current_user
router = APIRouter()
@router.post("/login", response_model=Token)
def login(
db: Session = Depends(get_db),
form_data: OAuth2PasswordRequestForm = Depends()
):
# 驗證用戶
user = get_user_by_email(db, email=form_data.username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Bearer"},
)
if not verify_password(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Bearer"},
)
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Inactive user",
headers={"WWW-Authenticate": "Bearer"},
)
# 創(chuàng)建訪問令牌
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.email}, expires_delta=access_token_expires
)
return {
"access_token": access_token,
"token_type": "bearer",
"user": User.from_orm(user)
}
@router.post("/register", response_model=Token)
def register(
*,
db: Session = Depends(get_db),
user_in: UserCreate,
):
# 檢查郵箱是否已存在
user = get_user_by_email(db, email=user_in.email)
if user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="The user with this email already exists in the system.",
)
# 創(chuàng)建用戶
from app.crud.user import create_user
user = create_user(db=db, user=user_in)
# 創(chuàng)建訪問令牌
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.email}, expires_delta=access_token_expires
)
return {
"access_token": access_token,
"token_type": "bearer",
"user": User.from_orm(user)
}
@router.get("/me", response_model=User)
def read_users_me(current_user: User = Depends(get_current_user)):
return current_user
4.2 用戶管理端點
實現(xiàn)用戶的增刪改查功能。
# app/api/endpoints/users.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from app.db.session import get_db
from app.crud.user import get_user, get_users, create_user, update_user, delete_user
from app.schemas.user import User, UserCreate, UserUpdate
from app.api.deps import get_current_active_superuser
router = APIRouter()
@router.get("/", response_model=List[User])
def read_users(
db: Session = Depends(get_db),
skip: int = 0,
limit: int = 100,
current_user: User = Depends(get_current_active_superuser),
):
users = get_users(db, skip=skip, limit=limit)
return users
@router.post("/", response_model=User)
def create_user_endpoint(
*,
db: Session = Depends(get_db),
user_in: UserCreate,
current_user: User = Depends(get_current_active_superuser),
):
user = get_user_by_email(db, email=user_in.email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this email already exists in the system.",
)
user = create_user(db=db, user=user_in)
return user
@router.get("/{user_id}", response_model=User)
def read_user(
user_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_superuser),
):
user = get_user(db, user_id=user_id)
if not user:
raise HTTPException(
status_code=404,
detail="User not found",
)
return user
@router.put("/{user_id}", response_model=User)
def update_user_endpoint(
*,
user_id: int,
user_in: UserUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_superuser),
):
user = get_user(db, user_id=user_id)
if not user:
raise HTTPException(
status_code=404,
detail="User not found",
)
user = update_user(db=db, user_id=user_id, user=user_in)
return user
@router.delete("/{user_id}")
def delete_user_endpoint(
user_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_superuser),
):
user = get_user(db, user_id=user_id)
if not user:
raise HTTPException(
status_code=404,
detail="User not found",
)
delete_user(db=db, user_id=user_id)
return {"message": "User deleted successfully"}
4.3 依賴項管理
集中管理API端點依賴項。
# app/api/deps.py
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.core.security import decode_access_token
from app.crud.user import get_user_by_email
from app.schemas.user import User
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"/api/v1/auth/login")
def get_current_user(
db: Session = Depends(get_db),
token: str = Depends(oauth2_scheme)
):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = decode_access_token(token)
if payload is None:
raise credentials_exception
email: str = payload.get("sub")
if email is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = get_user_by_email(db, email=email)
if user is None:
raise credentials_exception
return user
def get_current_active_user(current_user: User = Depends(get_current_user)):
if not current_user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
def get_current_active_superuser(current_user: User = Depends(get_current_active_user)):
if not current_user.is_superuser:
raise HTTPException(
status_code=400,
detail="The user doesn't have enough privileges"
)
return current_user
5. 數(shù)據(jù)庫設(shè)置與遷移
5.1 數(shù)據(jù)庫會話管理
# app/db/session.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from app.core.config import settings
# 創(chuàng)建數(shù)據(jù)庫引擎
engine = create_engine(
settings.DATABASE_URL,
connect_args={"check_same_thread": False} # 僅SQLite需要
)
# 創(chuàng)建會話本地類
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 聲明基類
Base = declarative_base()
# 依賴項:獲取數(shù)據(jù)庫會話
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
5.2 數(shù)據(jù)庫遷移配置
使用Alembic進行數(shù)據(jù)庫遷移管理。
# alembic/env.py
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from app.core.config import settings
from app.models.user import Base # 導(dǎo)入所有模型
# Alembic配置
config = context.config
config.set_main_option("sqlalchemy.url", settings.DATABASE_URL)
# 設(shè)置目標(biāo)元數(shù)據(jù)
target_metadata = Base.metadata
def run_migrations_offline():
"""在離線模式下運行遷移"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""在在線模式下運行遷移"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
6. 測試與調(diào)試
6.1 編寫單元測試
使用pytest編寫API端點測試。
# tests/test_users.py
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.main import app
from app.db.session import Base, get_db
from app.core.security import get_password_hash
from app.models.user import User
# 測試數(shù)據(jù)庫
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 創(chuàng)建測試數(shù)據(jù)庫表
Base.metadata.create_all(bind=engine)
# 重寫get_db依賴
def override_get_db():
try:
db = TestingSessionLocal()
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_create_user():
response = client.post(
"/api/v1/auth/register",
json={
"email": "test@example.com",
"password": "password",
"full_name": "Test User"
},
)
assert response.status_code == 200
data = response.json()
assert data["user"]["email"] == "test@example.com"
assert "access_token" in data
assert data["token_type"] == "bearer"
def test_login_user():
# 首先創(chuàng)建用戶
client.post(
"/api/v1/auth/register",
json={
"email": "login@example.com",
"password": "password",
"full_name": "Login User"
},
)
# 測試登錄
response = client.post(
"/api/v1/auth/login",
data={
"username": "login@example.com",
"password": "password"
},
)
assert response.status_code == 200
data = response.json()
assert data["user"]["email"] == "login@example.com"
assert "access_token" in data
def test_get_users_without_auth():
response = client.get("/api/v1/users/")
assert response.status_code == 401 # 未授權(quán)
@pytest.fixture(autouse=True)
def cleanup_db():
# 在每個測試運行前清理數(shù)據(jù)庫
db = TestingSessionLocal()
db.query(User).delete()
db.commit()
db.close()
6.2 使用FastAPI自動文檔
FastAPI自動生成交互式API文檔:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
7. 部署與性能優(yōu)化
7.1 使用Gunicorn和Uvicorn部署
創(chuàng)建生產(chǎn)環(huán)境部署配置。
# gunicorn_conf.py import multiprocessing # 服務(wù)器socket bind = "0.0.0.0:8000" # 工作進程數(shù) workers = multiprocessing.cpu_count() * 2 + 1 # 工作進程類型 worker_class = "uvicorn.workers.UvicornWorker" # 日志配置 loglevel = "info" accesslog = "-" # 標(biāo)準輸出 errorlog = "-" # 標(biāo)準錯誤輸出 # 超時設(shè)置 timeout = 120 keepalive = 5
7.2 Docker容器化
創(chuàng)建Dockerfile和docker-compose配置。
# Dockerfile
FROM python:3.9-slim
# 設(shè)置工作目錄
WORKDIR /app
# 設(shè)置環(huán)境變量
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# 安裝系統(tǒng)依賴
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 安裝Python依賴
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 復(fù)制項目文件
COPY . .
# 創(chuàng)建非root用戶
RUN adduser --disabled-password --gecos '' myuser
USER myuser
# 暴露端口
EXPOSE 8000
# 啟動命令
CMD ["gunicorn", "-c", "gunicorn_conf.py", "app.main:app"]
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydatabase
depends_on:
- db
volumes:
- .:/app
db:
image: postgres:13
environment:
- POSTGRES_DB=mydatabase
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
7.3 性能優(yōu)化建議
- 數(shù)據(jù)庫連接池:配置合適的連接池大小
- 查詢優(yōu)化:使用Eager Loading避免N+1查詢問題
- 緩存策略:使用Redis緩存頻繁訪問的數(shù)據(jù)
- 異步任務(wù):使用Celery處理耗時任務(wù)
- CDN加速:靜態(tài)資源使用CDN分發(fā)
- 索引優(yōu)化:為常用查詢字段添加數(shù)據(jù)庫索引
8. 完整代碼示例
以下是一個簡化的完整示例,展示了核心功能:
# simplified_main.py
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from typing import Optional
import secrets
# 配置
SECRET_KEY = secrets.token_urlsafe(32)
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# 模擬數(shù)據(jù)庫
fake_users_db = {}
# 密碼上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# 模型定義
class User(BaseModel):
username: str
email: Optional[str] = None
full_name: Optional[str] = None
disabled: Optional[bool] = None
class UserInDB(User):
hashed_password: str
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None
# 工具函數(shù)
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def authenticate_user(db, username: str, password: str):
user = get_user(db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# FastAPI應(yīng)用
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/users/me/", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_user)):
return current_user
# 初始化一個測試用戶
fake_users_db["testuser"] = {
"username": "testuser",
"full_name": "Test User",
"email": "test@example.com",
"hashed_password": get_password_hash("testpassword"),
"disabled": False,
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
9. 常見問題與解決方案
9.1 CORS問題
確保正確配置CORS中間件,允許前端域名訪問API。
9.2 數(shù)據(jù)庫連接池耗盡
調(diào)整數(shù)據(jù)庫連接池大小,確保連接正確釋放。
9.3 性能瓶頸
使用異步數(shù)據(jù)庫驅(qū)動,優(yōu)化查詢,添加緩存層。
9.4 安全性問題
- 使用HTTPS
- 驗證輸入數(shù)據(jù)
- 防止SQL注入(ORM通常已處理)
- 限制請求頻率
- 保護敏感信息
總結(jié)
Python后端API開發(fā)是一個綜合性的工程,涉及框架選擇、數(shù)據(jù)庫設(shè)計、安全認證、性能優(yōu)化等多個方面。FastAPI作為一個現(xiàn)代框架,提供了高性能、易用性和強大的功能,是開發(fā)API的優(yōu)秀選擇。
本文詳細介紹了從項目搭建到部署上線的完整流程,提供了最佳實踐和代碼示例。在實際開發(fā)中,應(yīng)根據(jù)項目需求選擇合適的工具和架構(gòu),注重代碼質(zhì)量和可維護性,同時充分考慮安全性和性能要求。
通過遵循本文的指導(dǎo),您可以構(gòu)建出健壯、高效且易于維護的Python后端API,為您的應(yīng)用程序提供強大的后端支持。
以上就是Python后端API開發(fā)的完全指南的詳細內(nèi)容,更多關(guān)于Python后端API開發(fā)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用Python高效獲取網(wǎng)絡(luò)數(shù)據(jù)的操作指南
網(wǎng)絡(luò)爬蟲是一種自動化程序,用于訪問和提取網(wǎng)站上的數(shù)據(jù),Python是進行網(wǎng)絡(luò)爬蟲開發(fā)的理想語言,擁有豐富的庫和工具,使得編寫和維護爬蟲變得簡單高效,本文將詳細介紹如何使用Python進行網(wǎng)絡(luò)爬蟲開發(fā),包括基本概念、常用庫、數(shù)據(jù)提取方法、反爬措施應(yīng)對以及實際案例2025-03-03
Python基于TCP實現(xiàn)會聊天的小機器人功能示例
這篇文章主要介紹了Python基于TCP實現(xiàn)會聊天的小機器人功能,結(jié)合實例形式分析了Python通過socket模塊實現(xiàn)TCP連接的客戶端與服務(wù)器端模擬聊天機器人功能相關(guān)操作技巧,需要的朋友可以參考下2018-04-04
關(guān)于pytorch中網(wǎng)絡(luò)loss傳播和參數(shù)更新的理解
今天小編就為大家分享一篇關(guān)于pytorch中網(wǎng)絡(luò)loss傳播和參數(shù)更新的理解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08
機器學(xué)習(xí)經(jīng)典算法-logistic回歸代碼詳解
這篇文章主要介紹了機器學(xué)習(xí)經(jīng)典算法-logistic回歸代碼詳解,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12
如何用Python Beautiful?Soup解析HTML內(nèi)容
Beautiful Soup是一種Python的解析庫,主要用于解析和處理HTML/XML內(nèi)容,詳細介紹Beautiful Soup的使用方式和應(yīng)用場景,本文給大家介紹的非常詳細,需要的朋友可以參考下2023-05-05
python opencv實現(xiàn)直線檢測并測出傾斜角度(附源碼+注釋)
這篇文章主要介紹了python opencv實現(xiàn)直線檢測并測出傾斜角度(附源碼+注釋),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12

