This commit is contained in:
aaron 2025-03-21 23:22:54 +08:00
parent 9d6ba6cc29
commit 42943f9967
12 changed files with 372 additions and 179 deletions

View File

@ -4,6 +4,9 @@ HOST=0.0.0.0
PORT=9001 PORT=9001
DEBUG=true DEBUG=true
# API响应格式配置
USE_STANDARD_RESPONSE=1
# 数据库配置 # 数据库配置
DB_HOST=localhost DB_HOST=localhost
DB_PORT=3306 DB_PORT=3306

View File

@ -235,13 +235,59 @@ docker exec -it ai-dressing-app bash -c "env | sort"
- 敏感信息应通过环境变量或Docker secrets进行管理 - 敏感信息应通过环境变量或Docker secrets进行管理
- 建议将`.env`文件添加到`.gitignore`中,避免意外提交 - 建议将`.env`文件添加到`.gitignore`中,避免意外提交
## API 文档 ## API接口文档
启动服务后,访问以下地址查看自动生成的 API 文档: 服务启动后可通过以下地址访问API文档:
- Swagger UI: http://localhost:9001/docs - Swagger UI: http://localhost:9001/docs
- ReDoc: http://localhost:9001/redoc - ReDoc: http://localhost:9001/redoc
## API响应格式
系统支持两种API响应格式可以通过环境变量进行配置
### 标准响应格式 (USE_STANDARD_RESPONSE=1)
所有API接口都会返回统一的标准格式
```json
{
"code": 200,
"message": "操作成功",
"data": { ... }
}
```
错误响应示例:
```json
{
"code": 400,
"message": "请求参数错误",
"data": null
}
```
### 简洁响应格式 (USE_STANDARD_RESPONSE=0)
API接口直接返回数据不包含额外的包装
```json
{ ... } // 直接返回业务数据
```
错误响应仍然保持标准格式,以确保错误信息清晰:
```json
{
"code": 400,
"message": "请求参数错误",
"data": null
}
```
> 注意:健康检查端点 `/health``/` 始终返回简洁格式,不受环境变量影响。
## 主要 API 端点 ## 主要 API 端点
### 大模型对话 ### 大模型对话

View File

@ -1,90 +1,82 @@
from fastapi import FastAPI, Request, status from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException from starlette.exceptions import HTTPException as StarletteHTTPException
import os
import traceback
import logging import logging
from typing import Any, Dict, Optional, Union
from app.utils.response import APIResponse from app.models.api_response import APIResponseModel
# 根据环境变量决定是否使用标准响应格式
use_standard_response = os.environ.get("USE_STANDARD_RESPONSE") == "1"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class CustomHTTPException(Exception): class CustomHTTPException(Exception):
"""自定义HTTP异常""" """自定义HTTP异常类用于抛出自定义的HTTP错误"""
def __init__(self, status_code: int, detail: str = None):
def __init__(
self,
status_code: int = 400,
code: int = None,
message: str = "操作失败",
data: Any = None
):
self.status_code = status_code self.status_code = status_code
self.code = code or status_code self.detail = detail
self.message = message
self.data = data
super().__init__(self.message)
def setup_exception_handlers(app: FastAPI) -> None: async def http_exception_handler(request: Request, exc: CustomHTTPException):
"""设置FastAPI应用的异常处理程序""" """处理自定义HTTP异常的处理器"""
logger.error(f"HTTP错误: {exc.status_code} - {exc.detail}")
@app.exception_handler(CustomHTTPException)
async def custom_http_exception_handler(request: Request, exc: CustomHTTPException):
"""处理自定义HTTP异常"""
logger.error(f"自定义HTTP异常: {exc.message} (状态码: {exc.status_code}, 业务码: {exc.code})")
return JSONResponse( return JSONResponse(
status_code=exc.status_code, status_code=exc.status_code,
content=APIResponse.error( content=APIResponseModel(
message=exc.message, code=exc.status_code,
code=exc.code, message=exc.detail or "请求处理失败"
data=exc.data ).dict()
)
) )
@app.exception_handler(StarletteHTTPException) async def starlette_exception_handler(request: Request, exc: StarletteHTTPException):
async def http_exception_handler(request: Request, exc: StarletteHTTPException): """处理FastAPI内置的HTTP异常的处理器"""
"""处理标准HTTP异常""" logger.error(f"Starlette HTTP错误: {exc.status_code} - {exc.detail}")
logger.error(f"HTTP异常: {exc.detail} (状态码: {exc.status_code})")
return JSONResponse( return JSONResponse(
status_code=exc.status_code, status_code=exc.status_code,
content=APIResponse.error( content=APIResponseModel(
message=str(exc.detail), code=exc.status_code,
code=exc.status_code message=str(exc.detail)
) ).dict()
) )
@app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError):
async def validation_exception_handler(request: Request, exc: RequestValidationError): """处理请求验证错误的处理器"""
"""处理请求验证异常""" error_detail = exc.errors()
error_details = exc.errors() error_messages = [f"{e['loc'][-1]}: {e['msg']}" for e in error_detail]
error_messages = [] error_message = ", ".join(error_messages)
logger.error(f"请求验证错误: {error_message}")
for error in error_details:
location = " -> ".join(str(loc) for loc in error["loc"])
message = f"{location}: {error['msg']}"
error_messages.append(message)
error_message = "; ".join(error_messages)
logger.error(f"验证错误: {error_message}")
return JSONResponse( return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, status_code=422,
content=APIResponse.error( content=APIResponseModel(
message="请求参数验证失败", code=422,
code=status.HTTP_422_UNPROCESSABLE_ENTITY, message=f"请求参数验证失败: {error_message}",
data={"details": error_details} data=error_detail
) ).dict()
) )
@app.exception_handler(Exception) async def general_exception_handler(request: Request, exc: Exception):
async def general_exception_handler(request: Request, exc: Exception): """处理一般性异常的处理器"""
"""处理通用异常""" # 记录详细的错误信息
logger.exception(f"未处理的异常: {str(exc)}") error_msg = str(exc)
logger.error(f"服务器错误: {error_msg}")
logger.error(traceback.format_exc())
return JSONResponse( return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=500,
content=APIResponse.error( content=APIResponseModel(
code=500,
message="服务器内部错误", message="服务器内部错误",
code=status.HTTP_500_INTERNAL_SERVER_ERROR, data={"detail": error_msg} if os.environ.get("DEBUG") == "1" else None
data={"detail": str(exc)} if app.debug else None ).dict()
)
) )
def setup_exception_handlers(app: FastAPI):
"""为FastAPI应用设置所有异常处理器"""
app.add_exception_handler(CustomHTTPException, http_exception_handler)
app.add_exception_handler(StarletteHTTPException, starlette_exception_handler)
app.add_exception_handler(RequestValidationError, validation_exception_handler)
app.add_exception_handler(Exception, general_exception_handler)

View File

@ -24,6 +24,10 @@ logging.basicConfig(
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# 全局配置:是否使用标准响应格式,默认禁用直接返回数据
os.environ["USE_STANDARD_RESPONSE"] = os.environ.get("USE_STANDARD_RESPONSE", "0")
logger.info(f"标准响应格式: {'已启用' if os.environ['USE_STANDARD_RESPONSE'] == '1' else '已禁用'}")
app = FastAPI( app = FastAPI(
title="AI-Dressing API", title="AI-Dressing API",
description="基于 DashScope 的 AI 服务 API", description="基于 DashScope 的 AI 服务 API",
@ -53,7 +57,7 @@ app.include_router(tryon_router.router, prefix="/api/tryons", tags=["试穿"])
@app.get("/", tags=["健康检查"]) @app.get("/", tags=["健康检查"])
async def root(): async def root():
"""API 根端点""" """API 根端点"""
return APIResponse.ok(message="服务运行中") return {"message": "服务运行中"}
@app.get("/health", tags=["健康检查"]) @app.get("/health", tags=["健康检查"])
async def health_check(): async def health_check():
@ -86,9 +90,17 @@ async def health_check():
health_data["checks"]["qcloud"] = {"status": "missing", "message": "腾讯云凭证未配置"} health_data["checks"]["qcloud"] = {"status": "missing", "message": "腾讯云凭证未配置"}
if health_status == "healthy": if health_status == "healthy":
return APIResponse.ok(data=health_data, message=health_message) return {
"status": health_status,
"message": health_message,
"data": health_data
}
else: else:
return APIResponse.error(message=health_message, code=503, data=health_data) return {
"status": health_status,
"message": health_message,
"data": health_data
}
@app.get("/info", tags=["服务信息"]) @app.get("/info", tags=["服务信息"])
async def get_info(): async def get_info():
@ -98,8 +110,9 @@ async def get_info():
"app_name": "AI-Dressing API", "app_name": "AI-Dressing API",
"version": "0.1.0", "version": "0.1.0",
"debug_mode": settings.debug, "debug_mode": settings.debug,
"standard_response": os.environ.get("USE_STANDARD_RESPONSE") == "1"
} }
return APIResponse.ok(data=info_data, message="服务信息获取成功") return info_data
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn

66
app/middleware.py Normal file
View File

@ -0,0 +1,66 @@
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response, JSONResponse
import json
import os
from app.models.api_response import APIResponseModel
from app.utils.response import APIResponse
class ResponseWrapperMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# 检查是否需要启用标准响应包装
use_standard_response = os.environ.get("USE_STANDARD_RESPONSE") == "1"
# 不包装的情况:健康检查端点或禁用了标准响应
health_endpoints = ["/health", "/"]
if request.url.path in health_endpoints or not use_standard_response:
return await call_next(request)
response = await call_next(request)
# 如果响应已经被包装或者是文件下载等特殊响应,则不再处理
if (
isinstance(response, Response)
and not isinstance(response, JSONResponse)
or response.headers.get("content-type") != "application/json"
):
return response
# 处理JSON响应
try:
response_body = [section async for section in response.body_iterator]
response.body_iterator = None
if len(response_body) > 0:
body = response_body[0].decode()
json_body = json.loads(body)
# 检查是否已经是标准格式
if isinstance(json_body, dict) and "code" in json_body and "data" in json_body and "message" in json_body:
# 已经是标准格式,直接返回
new_response = Response(
content=body,
status_code=response.status_code,
headers=dict(response.headers),
media_type=response.media_type
)
return new_response
# 包装响应
api_response = APIResponseModel(
code=200,
message="操作成功",
data=json_body
)
return JSONResponse(
content=api_response.dict(),
status_code=response.status_code,
headers=dict(response.headers),
)
return response
except Exception as e:
# 如果解析失败,返回原始响应
return response

View File

@ -3,6 +3,9 @@ from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware
from typing import Any, Dict, Optional, Union from typing import Any, Dict, Optional, Union
import json import json
import os
from app.utils.response import APIResponse
class ResponseWrapperMiddleware(BaseHTTPMiddleware): class ResponseWrapperMiddleware(BaseHTTPMiddleware):
""" """
@ -14,11 +17,22 @@ class ResponseWrapperMiddleware(BaseHTTPMiddleware):
"message": "操作成功", "message": "操作成功",
"data": 原始响应数据 "data": 原始响应数据
} }
可以通过设置环境变量USE_STANDARD_RESPONSE=0来禁用标准响应格式
""" """
def __init__(self, app):
super().__init__(app)
# 从环境变量中读取是否启用标准响应格式
self.use_standard_response = os.environ.get("USE_STANDARD_RESPONSE", "1") == "1"
async def dispatch( async def dispatch(
self, request: Request, call_next self, request: Request, call_next
) -> Response: ) -> Response:
# 如果禁用了标准响应,直接返回原始响应
if not self.use_standard_response:
return await call_next(request)
# 排除不需要包装的路径 # 排除不需要包装的路径
if self._should_skip_path(request.url.path): if self._should_skip_path(request.url.path):
return await call_next(request) return await call_next(request)

View File

@ -1 +1 @@
from app.models.api_response import APIResponseModel, PaginatedResponseModel

View File

@ -0,0 +1,27 @@
from typing import Any, Dict, List, Optional, Union, ClassVar
from pydantic import BaseModel, Field
from datetime import datetime
class APIResponseModel(BaseModel):
"""API响应基础模型"""
code: int = Field(200, description="业务状态码")
message: str = Field("操作成功", description="响应消息")
data: Optional[Any] = Field(None, description="响应数据")
class Config:
"""模型配置"""
arbitrary_types_allowed = True
json_encoders = {
datetime: lambda dt: dt.isoformat()
}
class PaginatedResponseModel(BaseModel):
"""分页响应模型"""
items: List[Any] = Field(..., description="数据项列表")
total: int = Field(..., description="总数据量")
page: int = Field(1, description="当前页码")
size: int = Field(10, description="每页数据量")
class Config:
"""模型配置"""
arbitrary_types_allowed = True

View File

@ -4,7 +4,7 @@ from typing import List
from app.database import get_db from app.database import get_db
from app.models.dress import Dress from app.models.dress import Dress
from app.schemas.dress import DressCreate, DressUpdate, DressResponse, DressListResponse from app.schemas.dress import DressCreate, DressUpdate, DressResponse
from app.utils.response import APIResponse from app.utils.response import APIResponse
router = APIRouter() router = APIRouter()
@ -17,12 +17,15 @@ def create_dress(dress: DressCreate, db: Session = Depends(get_db)):
db.add(db_dress) db.add(db_dress)
db.commit() db.commit()
db.refresh(db_dress) db.refresh(db_dress)
return APIResponse.created(data=db_dress, message="服装创建成功") return db_dress
except Exception as e: except Exception as e:
db.rollback() db.rollback()
return APIResponse.error(message=f"服装创建失败: {str(e)}", code=500) raise HTTPException(
status_code=500,
detail=f"服装创建失败: {str(e)}"
)
@router.get("/", response_model=DressListResponse) @router.get("/", response_model=List[DressResponse])
def get_all_dresses( def get_all_dresses(
skip: int = 0, skip: int = 0,
limit: int = 100, limit: int = 100,
@ -32,28 +35,32 @@ def get_all_dresses(
try: try:
dresses = db.query(Dress).offset(skip).limit(limit).all() dresses = db.query(Dress).offset(skip).limit(limit).all()
total = db.query(Dress).count() total = db.query(Dress).count()
return APIResponse.ok(
data={ # 构建分页数据
result = {
"items": dresses, "items": dresses,
"total": total, "total": total,
"page": skip // limit + 1 if limit > 0 else 1, "page": skip // limit + 1 if limit > 0 else 1,
"size": limit "size": limit
}, }
message="服装列表获取成功"
) return result
except Exception as e: except Exception as e:
return APIResponse.error(message=f"获取服装列表失败: {str(e)}", code=500) raise HTTPException(
status_code=500,
detail=f"获取服装列表失败: {str(e)}"
)
@router.get("/{dress_id}", response_model=DressResponse) @router.get("/{dress_id}", response_model=DressResponse)
def get_dress(dress_id: int, db: Session = Depends(get_db)): def get_dress(dress_id: int, db: Session = Depends(get_db)):
"""获取单个服装记录""" """获取单个服装记录"""
try:
dress = db.query(Dress).filter(Dress.id == dress_id).first() dress = db.query(Dress).filter(Dress.id == dress_id).first()
if dress is None: if dress is None:
return APIResponse.not_found(message=f"未找到ID为{dress_id}的服装") raise HTTPException(
return APIResponse.ok(data=dress, message="服装获取成功") status_code=status.HTTP_404_NOT_FOUND,
except Exception as e: detail=f"未找到ID为{dress_id}的服装"
return APIResponse.error(message=f"获取服装失败: {str(e)}", code=500) )
return dress
@router.put("/{dress_id}", response_model=DressResponse) @router.put("/{dress_id}", response_model=DressResponse)
def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db)): def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db)):
@ -61,7 +68,10 @@ def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db
try: try:
db_dress = db.query(Dress).filter(Dress.id == dress_id).first() db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
if db_dress is None: if db_dress is None:
return APIResponse.not_found(message=f"未找到ID为{dress_id}的服装") raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"未找到ID为{dress_id}的服装"
)
# 更新服装字段 # 更新服装字段
for field, value in dress.dict(exclude_unset=True).items(): for field, value in dress.dict(exclude_unset=True).items():
@ -69,22 +79,37 @@ def update_dress(dress_id: int, dress: DressUpdate, db: Session = Depends(get_db
db.commit() db.commit()
db.refresh(db_dress) db.refresh(db_dress)
return APIResponse.ok(data=db_dress, message="服装更新成功") return db_dress
except HTTPException:
db.rollback()
raise
except Exception as e: except Exception as e:
db.rollback() db.rollback()
return APIResponse.error(message=f"更新服装失败: {str(e)}", code=500) raise HTTPException(
status_code=500,
detail=f"更新服装失败: {str(e)}"
)
@router.delete("/{dress_id}") @router.delete("/{dress_id}", status_code=status.HTTP_200_OK)
def delete_dress(dress_id: int, db: Session = Depends(get_db)): def delete_dress(dress_id: int, db: Session = Depends(get_db)):
"""删除服装记录""" """删除服装记录"""
try: try:
db_dress = db.query(Dress).filter(Dress.id == dress_id).first() db_dress = db.query(Dress).filter(Dress.id == dress_id).first()
if db_dress is None: if db_dress is None:
return APIResponse.not_found(message=f"未找到ID为{dress_id}的服装") raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"未找到ID为{dress_id}的服装"
)
db.delete(db_dress) db.delete(db_dress)
db.commit() db.commit()
return APIResponse.ok(message="服装删除成功") return {"message": "服装删除成功"}
except HTTPException:
db.rollback()
raise
except Exception as e: except Exception as e:
db.rollback() db.rollback()
return APIResponse.error(message=f"删除服装失败: {str(e)}", code=500) raise HTTPException(
status_code=500,
detail=f"删除服装失败: {str(e)}"
)

View File

@ -24,7 +24,7 @@ router = APIRouter()
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY") DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
DASHSCOPE_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/image-synthesis" DASHSCOPE_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/image-synthesis"
@router.post("/", response_model=TryOnResponse, status_code=201) @router.post("", response_model=TryOnResponse, status_code=201)
async def create_tryon( async def create_tryon(
tryon_data: TryOnCreate, tryon_data: TryOnCreate,
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
@ -63,7 +63,7 @@ async def create_tryon(
person_image_url=tryon_data.person_image_url person_image_url=tryon_data.person_image_url
) )
return APIResponse.ok(data=db_tryon, message="试穿记录创建成功") return db_tryon
except Exception as e: except Exception as e:
logger.error(f"创建试穿记录失败: {str(e)}") logger.error(f"创建试穿记录失败: {str(e)}")
db.rollback() db.rollback()
@ -120,7 +120,8 @@ async def get_tryons(
""" """
try: try:
tryons = db.query(TryOn).order_by(TryOn.id.desc()).offset(skip).limit(limit).all() tryons = db.query(TryOn).order_by(TryOn.id.desc()).offset(skip).limit(limit).all()
return APIResponse.ok(data=tryons, message="试穿记录列表获取成功")
return tryons
except Exception as e: except Exception as e:
logger.error(f"获取试穿记录列表失败: {str(e)}") logger.error(f"获取试穿记录列表失败: {str(e)}")
raise HTTPException(status_code=500, detail=f"获取试穿记录列表失败: {str(e)}") raise HTTPException(status_code=500, detail=f"获取试穿记录列表失败: {str(e)}")
@ -139,7 +140,7 @@ async def get_tryon(
tryon = db.query(TryOn).filter(TryOn.id == tryon_id).first() tryon = db.query(TryOn).filter(TryOn.id == tryon_id).first()
if not tryon: if not tryon:
raise HTTPException(status_code=404, detail=f"未找到ID为{tryon_id}的试穿记录") raise HTTPException(status_code=404, detail=f"未找到ID为{tryon_id}的试穿记录")
return APIResponse.ok(data=tryon, message="试穿记录获取成功") return tryon
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
@ -172,7 +173,7 @@ async def update_tryon(
db.commit() db.commit()
db.refresh(db_tryon) db.refresh(db_tryon)
return APIResponse.ok(data=db_tryon, message="试穿记录更新成功") return db_tryon
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
@ -217,7 +218,7 @@ async def check_tryon_status(
except Exception as e: except Exception as e:
logger.error(f"调用DashScope API检查任务状态失败: {str(e)}") logger.error(f"调用DashScope API检查任务状态失败: {str(e)}")
return APIResponse.ok(data=db_tryon, message="试穿任务状态检查成功") return db_tryon
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:

View File

@ -1,5 +1,5 @@
from pydantic import BaseModel, Field, HttpUrl from pydantic import BaseModel, Field, HttpUrl
from typing import Optional, List from typing import Optional, List, Any, Dict, Union
from datetime import datetime from datetime import datetime
from enum import Enum from enum import Enum
from app.models.dress import GarmentType from app.models.dress import GarmentType
@ -39,8 +39,9 @@ class DressResponse(DressInDB):
"""服装API响应模型""" """服装API响应模型"""
pass pass
class DressListResponse(BaseModel): class PaginatedResponse(BaseModel):
items: List[DressResponse] """通用分页响应"""
items: List[Any]
total: int total: int
page: int page: int
size: int size: int
@ -50,7 +51,6 @@ class DressListResponse(BaseModel):
class StandardResponse(BaseModel): class StandardResponse(BaseModel):
"""标准API响应格式""" """标准API响应格式"""
success: bool = True
code: int = 200 code: int = 200
message: str = "操作成功" message: str = "操作成功"
data: Optional[dict] = None data: Optional[Any] = None

View File

@ -1,58 +1,57 @@
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union, ClassVar
from fastapi import status from fastapi import status
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import os
from app.models.api_response import APIResponseModel
class APIResponse: class APIResponse:
"""API标准响应格式工具类""" """API标准响应格式工具类"""
@staticmethod # 全局开关,控制是否使用标准响应格式
def success( USE_STANDARD_RESPONSE = os.environ.get("USE_STANDARD_RESPONSE", "1") == "1"
data: Any = None,
message: str = "操作成功",
code: int = 200
) -> Dict[str, Any]:
"""
成功响应
Args:
data: 响应数据
message: 响应消息
code: 状态码
Returns:
标准响应格式的字典
"""
return {
"success": True,
"code": code,
"message": message,
"data": data
}
@staticmethod @staticmethod
def error( def ok(data: Any = None, message: str = "操作成功") -> Dict[str, Any]:
message: str = "操作失败", """成功响应"""
code: int = 400, if not APIResponse.USE_STANDARD_RESPONSE:
data: Any = None return data
) -> Dict[str, Any]:
"""
错误响应
Args: return APIResponseModel(
message: 错误消息 code=200,
code: 错误状态码 message=message,
data: 附加错误数据 data=data
).dict()
Returns: @staticmethod
标准响应格式的字典 def error(message: str = "操作失败", code: int = 400, data: Any = None) -> Dict[str, Any]:
"""错误响应"""
# 错误响应即使禁用了标准格式也返回标准格式,确保错误信息清晰
return APIResponseModel(
code=code,
message=message,
data=data
).dict()
@staticmethod
def format_response(data: Any, message: str = "操作成功", code: int = 200) -> Dict:
""" """
return { 根据环境变量决定是否返回标准格式的响应
"success": False,
"code": code, 如果USE_STANDARD_RESPONSE=1返回标准格式
"message": message, {
"data": data "code": 200,
"message": "操作成功",
"data": {...}
} }
如果USE_STANDARD_RESPONSE=0直接返回data
"""
if APIResponse.USE_STANDARD_RESPONSE:
return APIResponseModel(code=code, message=message, data=data).dict()
else:
return data if data is not None else {"message": message}
@staticmethod @staticmethod
def json_response( def json_response(
data: Any = None, data: Any = None,
@ -76,12 +75,18 @@ class APIResponse:
Returns: Returns:
JSONResponse对象 JSONResponse对象
""" """
content = { if not APIResponse.USE_STANDARD_RESPONSE and success:
"success": success, return JSONResponse(
"code": code, content=data,
"message": message, status_code=status_code,
"data": data headers=headers
} )
content = APIResponseModel(
code=code,
message=message,
data=data
).dict()
return JSONResponse( return JSONResponse(
content=content, content=content,
@ -90,25 +95,26 @@ class APIResponse:
) )
# 常用响应码封装 # 常用响应码封装
@classmethod
def ok(cls, data: Any = None, message: str = "操作成功") -> Dict[str, Any]:
"""200 成功"""
return cls.success(data, message, 200)
@classmethod @classmethod
def created(cls, data: Any = None, message: str = "创建成功") -> Dict[str, Any]: def created(cls, data: Any = None, message: str = "创建成功") -> Dict[str, Any]:
"""201 创建成功""" """201 创建成功"""
return cls.success(data, message, 201) if not cls.USE_STANDARD_RESPONSE:
return data
return cls.ok(data, message)
@classmethod @classmethod
def accepted(cls, data: Any = None, message: str = "请求已接受") -> Dict[str, Any]: def accepted(cls, data: Any = None, message: str = "请求已接受") -> Dict[str, Any]:
"""202 已接受""" """202 已接受"""
return cls.success(data, message, 202) if not cls.USE_STANDARD_RESPONSE:
return data
return cls.ok(data, message)
@classmethod @classmethod
def no_content(cls) -> Dict[str, Any]: def no_content(cls) -> Dict[str, Any]:
"""204 无内容""" """204 无内容"""
return cls.success(None, "无内容", 204) if not cls.USE_STANDARD_RESPONSE:
return None
return cls.ok(None, "无内容")
@classmethod @classmethod
def bad_request(cls, message: str = "请求参数错误") -> Dict[str, Any]: def bad_request(cls, message: str = "请求参数错误") -> Dict[str, Any]: