mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2025-08-14 19:04:00 +08:00
77 lines
2.2 KiB
Python
77 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
import asyncio
|
|
import functools
|
|
import time
|
|
|
|
from math import ceil
|
|
from typing import Any, Callable
|
|
|
|
from fastapi import FastAPI, Request, Response
|
|
from fastapi.routing import APIRoute
|
|
|
|
from backend.common.exception import errors
|
|
from backend.common.log import log
|
|
from backend.common.response.response_code import StandardResponseCode
|
|
|
|
|
|
def ensure_unique_route_names(app: FastAPI) -> None:
|
|
"""
|
|
检查路由名称是否唯一
|
|
|
|
:param app: FastAPI 应用实例
|
|
:return:
|
|
"""
|
|
temp_routes = set()
|
|
for route in app.routes:
|
|
if isinstance(route, APIRoute):
|
|
if route.name in temp_routes:
|
|
raise ValueError(f'Non-unique route name: {route.name}')
|
|
temp_routes.add(route.name)
|
|
|
|
|
|
async def http_limit_callback(request: Request, response: Response, expire: int) -> None:
|
|
"""
|
|
请求限制时的默认回调函数
|
|
|
|
:param request: FastAPI 请求对象
|
|
:param response: FastAPI 响应对象
|
|
:param expire: 剩余毫秒数
|
|
:return:
|
|
"""
|
|
expires = ceil(expire / 1000)
|
|
raise errors.HTTPError(
|
|
code=StandardResponseCode.HTTP_429, msg='请求过于频繁,请稍后重试', headers={'Retry-After': str(expires)}
|
|
)
|
|
|
|
|
|
def timer(func) -> Callable:
|
|
"""函数耗时计时装饰器"""
|
|
|
|
@functools.wraps(func)
|
|
async def async_wrapper(*args, **kwargs) -> Any:
|
|
start_time = time.perf_counter()
|
|
result = await func(*args, **kwargs)
|
|
elapsed_seconds = time.perf_counter() - start_time
|
|
_log_time(func, elapsed_seconds)
|
|
return result
|
|
|
|
@functools.wraps(func)
|
|
def sync_wrapper(*args, **kwargs) -> Any:
|
|
start_time = time.perf_counter()
|
|
result = func(*args, **kwargs)
|
|
elapsed_seconds = time.perf_counter() - start_time
|
|
_log_time(func, elapsed_seconds)
|
|
return result
|
|
|
|
def _log_time(func, elapsed: float):
|
|
# 智能选择单位(秒、毫秒、微秒、纳秒)
|
|
if elapsed >= 1:
|
|
unit, factor = 's', 1
|
|
else:
|
|
unit, factor = 'ms', 1e3
|
|
|
|
log.info(f'{func.__module__}.{func.__name__} | {elapsed * factor:.3f} {unit}')
|
|
|
|
return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper
|